Java generics puzzler with generic static factory - java

I recently started writing a generic object mapper for a project and ran into something I don't quite understand. Given the following:
public class G<X> {
public G(Class<X> c) { }
public void m(X x) { }
public static <T> G<T> create(Class<T> c) {
return new G<T>(c);
}
public static void main(String[] args) {
Object o = ""; // irrelevant!
G<?> t = create(o.getClass());
t.m(o);
}
}
I get the following compilation error:
m(capture#402 of ?) in G<capture#402 of ?> cannot be applied to (java.lang.Object)
I can't seem to figure out a way to properly cast t to make this compile. What am I missing? Using JDK 1.6.
EDIT:
This is not an academic question. I'm trying to write a mapper from hibernate objects to their corresponding DTO to be passed around in the REST layer. The assumption is that for each ORM object Foo, there might exist a class FooDTO that has a constructor that takes an instance of Foo as a parameter. The generic class that maps Foo to FooDTO will encapsulate this assumption and throw appropriate exceptions if FooDTO doesn't exist or doesn't have the proper constructor:
class Mapper<Foo,FooDTO> {
private final Constructor<FooDTO> dtoConstructor;
Mapper(Class<Foo> fooClass, Class<FooDTO> fooDTOClass){
// find the constructor of FooDTO or throw ...
}
public FooDTO map(Foo f){
return dtoConstructor.newInstance(f);
}
// this factory is for convenience when we don't know the type of FooDTO:
public static Mapper<X,Object> create(Class<X> fromClass){
Class<Object> dtoClass = // ... find it
return new Mapper<X,Object>(fromClass,dtoClass);
}
}
This seems to break if I pass a generic object class to create.
Note that my actual implementation has all FooDTO classes extends from a generic super class, i.e., the signature of Mapper is actually something like Mapper<Foo,DTO<Foo>>. I don't think that's relevant here.
EDIT 2:
Actually the suggestion of changing the line G<?> t = create(o.getClass()); to G<Object> t = (G<Object>) create(o.getClass()); worked in this context.
Unfortunately I didn't realize that the fact that my class is more complex actually has an impact. Here's a more complete example (I apologize for the piecemeal question):
public class Y<T> {
}
public class G<X, Z extends Y<X>> {
public G(Class<X> c, Class<Z> s) {
}
public void m(X x) {
}
public static <T, S extends Y<T>> G<T, S> create(Class<T> c) {
Class<S> s = null; // find this via some reflection magic
return new G<T, S>(c, s);
}
public static void main(String[] args) {
Object o = ""; // irrelevant!
G<? extends Object, Y<? extends Object>> t = create(o.getClass());
t.m(o);
}
}
In this case the object Class<S> is created using reflection and some conventional location for objects of that type. That part works fine and should be irrelevant to this discussion. The error I am getting now is the following:
inconvertible types
found : G<capture#155 of ? extends java.lang.Object,Y<capture#155 of ? extends java.lang.Object>>
required: G<java.lang.Object,Y<java.lang.Object>>
And if I change the incriminated line to:
G<Object, Y<Object>> t = (G<Object, Y<Object>>) create(o.getClass());
I get a similar error:
java: inconvertible types
required: G<java.lang.Object,Y<java.lang.Object>>
found: G<capture#1 of ? extends java.lang.Object,Y<capture#1 of ? extends java.lang.Object>>
Once again, I apologize for the piecemeal information. I am sorting through this while I am writing.

You have passed the Class object from the getClass() method, which returns a Class<?>, meaning that you had to declare t to be a G<?>.
You cannot call a method with a generic type parameter when the generic type parameter of the variable is a wildcard. The compiler doesn't know which specific class the wildcard really is, so it cannot guarantee type safety when such a method is called. It's the same reason that add can't be called on a List<?>.
To get this to compile, you must use a class literal, to avoid having a Class<?>, and declare t not to have a wildcard.
G<Object> t = create(Object.class);
Then
t.mo(o);
will compile.

What you have here is a consumer. However, the following seems to compile (in Eclipse).
public static class G<X, Z extends Y<X>> {
public G(Class<? extends X> c, Class<Z> s) {}
public void m(X x) {}
public static <T, S extends Y<T>> G<T, S> create(Class<? extends T> c) {
Class<S> s = null; // find this via some reflection magic
return new G<T, S>(c, s);
}
public static void main(String[] args) {
Object o = ""; // irrelevant!
create(o.getClass()).m(o);
}
}

You're creating a G<Object> and then assigning it to a variable of type G<?>. The method invoked takes a variable of the generic type, which won't take anything for <?>. If you change the variable to G<Object> it will work.

Since you are specifying G<?>, javac is expecting to figure out what the generics are (what classes do they represent). Changing the statement to G t = create(o.getClass()); fixes the errors.
capture errors generally mean that the compiler is unable to figure out the classes...
Its not really clear what you are trying to do... Perhaps that information would be useful in helping you more...

Related

Generic method return type - compilation error [duplicate]

This question already has an answer here:
Java generics : Type mismatch: cannot convert from Integer to K
(1 answer)
Closed 5 years ago.
Given this code sample:
class A {
}
public class TestGenerics {
private static <T extends A> T failsToCompile() {
return new A();
}
private static <T extends A> T compiles(T param) {
return param;
}
}
how come the first method doesn't compile, but the second one does?
The error is:
Incompatible types. Required: T. Found: A
Essentially what I'm trying to achieve is to return a concrete instance of a sub-type (for example of class B, that extends A). Sort of a factory method.
Edit: OK, for those who downvoted, I decided to elaborate a bit further. See the updated example, which no longer uses String (yes I'm aware that String is final, no need to state the obvious)
Edit 2: alternative version of the question:
How would you implement this method so that it compiles (without unchecked casts and warnings)?
abstract <T extends A> T failsToCompile();
Edit 3: Here's a code sample closer to the problem I'm trying to solve:
class Foo { }
class Bar extends Foo { }
class A<T extends Foo> { }
class B extends A<Bar> { }
public class TestGenerics {
private static <T extends Foo> A<T> failsToCompile() {
return new B();
}
}
Here, the method return type is A<T>. Considering that T is defined as a Foo or its subclass, and the definition of B is as follows: B extends A<Bar>, why can't I just return new B()? I guess the problem comes down to the fact that you can't assign List<Animal> dList = new ArrayList<Dog>(); and I understand why. But is there an elegant solution to this?
Ignoring the fact that String is final (so you can't have a sub-class of String), you can pass the generic class Class<T> into the method for the generic case. Like,
private static <T extends String> T get(Class<T> cls)
throws InstantiationException, IllegalAccessException {
return cls.newInstance();
}
I'm trying to elaborate my comment a bit more into an answer to the "why".
What you're showing in your question are generic methods (and not generic types), see e.g. Oracles "The Java™ Tutorials", Generic Methods.
Reasoning
Let's consider the explicit call for the working method: TestGenerics.<A>compiles(new A());
This instantiates the generic type T of your method call to class A. Remember that type erasure removes all these funky generic types when compiling, that the JVM doesn't see generic types and that the compiler handles generics. The compiler knows now, that you want to call the method with the generic type T "set" to class A. From that, it can deduct that the returned type is also of class A (as T is returned according to the methods code and as T is instantiated to A). That's all you gain here from using generics.
You can remove the <A> from your method call and make it look like an "ordinary" method call, as the compiler can deduct that T has to be A: TestGenerics.compiles(new A());
Let's look at TestGenerics.<B>compiles(new B()); with class B extends A {}. As above, the compiler knows that this method call will return an object of class B.
Now imagine the (not compiling) code TestGenerics.<B>compiles(new A());. The compiler will throw an error as the object passed to the method is not of type B. Otherwise the method would return an object of class A - but the generic type asserts that the method returns here an object of typeB.
That's actually equivalent to the example (B b = failsToCompile()) Andreas gave in his comment.
Until you instantiate a generic type - even if you set bounds (like T extends A) - it doesn't have a class type. You therefore can't return a concrete class, as this would not satisfy the generic type parameter.
Pizza Example
Just for fun, let's try to make a real world example for above reasoning: Pizza. For the sake of the example, let's assume that every Pizza is a subtype of the Margherita, i.e. you add ingredients to a Margherita to get your favorite other pizza.
class PizzaMargherita {};
class PizzaProsciutto {
PizzaProsciutto() {
super();
addIngredient(new Prosciutto());
}
}
Your compiles() method now bakes the pizza:
public static <P extends Margherita> P bake(P pizza) {
heat(pizza);
return pizza;
}
Bake a Margherita and get a (baked) Margherita out of your oven, bake a Prosciutto and get a Prosciutto.
Now think of this:
public static <P extends Margherita> P doesntBake(P pizza) {
return new PizzaMargherita();
}
An oven always returning a Margherita, independent of what you put into it ?!? The compiler doesn't approve that.
-> You need the concrete pizza to bake it or you need a workaround, like the type token:
public static <P extends Margherita> P bakePizza(Ingredients<P> ingredient) {
P pizza = buildPizza(ingredients);
return bake(pizza);
}
Workaround type token
You have to use a runtime-type token as #Elliott Frisch shows in his answer:
private static <T extends A> T failedToCompile(Class<T> c)
throws InstantiationException, IllegalAccessException {
return c.newInstance();
}
You can't instantiate a generic type - new T() doesn't work, as the JVM doesn't know anything about the generic type T. What you're looking for in edit 2 therefore doesn't work. But TestGenerics.<A>failedToCompile(A.class); works, as A.class is part of the java byte code.
Workaround type token & generic class
Depending on your specific requirements, a generic factory class might help you:
class Factory<T extends A> {
private final Class<T> typeToken;
public Factory(Class<T> typeToken) {
this.typeToken = typeToken;
}
public T build()
throws InstantiationException, IllegalAccessException {
return this.typeToken.newInstance();
}
}
You will still need some form of Map to get the correct factory to build the class you need, but you can now use whatever is available at the point where you need to create the Object of type T.
Map<String, Factory<?>> factories = new HashMap<>();
Map<DAO, Factory<?>> factories = new HashMap<>();
Map<ValueObject, Factory<?>> factories = new HashMap<>();
factories.get(valueObjectTypeA);
Others have clearly explained why the failsToCompile method doesn't compile. Andreas has even shown an example...
As to how to circumvect this, I think you should consider using a Supplier, which is just the factory you need:
private static <T extends A> T nowItCompilesFine(Supplier<? extends T> factory) {
return factory.get();
}
And you call it:
A a = nowItCompilesFine(() -> new A());
Or using a reference to a constructor:
A a = nowItCompilesFine(A::new);
Same with any descendant of A:
class Child extends A { }
List<Supplier<? extends A>> factories = Arrays.asList(A::new, Child::new);
Supplier<? extends A> factoryA = factories.get(0);
A a1 = nowItCompilesFine(factoryA);
Supplier<? extends A> factoryChild = factories.get(1);
A a2 = nowItCompilesFine(factoryChild);
System.out.println(a2.getClass().getSimpleName()); // Child

Passing parameterized Class instance to the constructor

I have lost in the Jungle of Generics, please help me :) I have something like this:
public class BaseClass<TYPE> {
public BaseClass(Class<TYPE> clazz) {};
}
public class FirstLevelClass<REFRESHABLE
extends RefreshableInterface> extends BaseClass<REFRESHABLE> {
public FirstLevelClass(Class<REFRESHABLE> clazz) {
super(clazz);
};
}
public class Argument<T extends AnyOtherClass>
implements RefreshableInterface {
public refresh() {}
}
pulbic class ProblematicClass
extends FirstLevelClass<Argument<AnyOtherClassDescendant>> {
public ProblematicClass() {
//Compiler error: Constructor
//FirstLevelClass<Argument<AnyOtherClassDescendant>>(Class<Argument>) is undefined
super(Argument.class);
}
}
As far as I think, the compiler should accept Argument since it implements RefreshableInterface.
Why do I get this error?
How can I make the ProblematicClass working?
ps: if you have better title for this, please change it. I could not make up better.
Issue is, your constructor expects a Class<T>, and T in your code is inferred as Argument<AnyOtherClassDescendant>.
So, you should pass a Class<Argument<AnyOtherClassDescendant>>, and you're passing Class<Argument>. But you can't pass that Class instance directly, as you cannot do Argument<AnyOtherClassDescendant>.class.
You can however, solve the issue by typecasting the class to required instance:
public ProblematicClass() {
super((Class<Argument<AnyOtherClassDescendant>>)(Class<?>)Argument.class);
}
Note, how you've to typecast Class<Argument> first to Class<?>, and then the resultant type to Class<Argument<AnyOtherClassDescendant>>. There is no simple way to achieve that.
The reason behind this is, there is only a single Class instance for all parameterized instantiation of a generic type, that is associated with the class itself. A single compilation unit of a generic type, compiles to just a single class file. I guess this is different in how C++ implements templates. There you get different machine codes for different instantiation.
So, if you execute the below code, you'll get true as output:
List<String> strList = new ArrayList<String>();
List<Integer> intList = new ArrayList<Integer>();
boolean isSameClassInstance = strList.getClass() == intList.getClass();
System.out.println(isSameClassInstance);

Forcing Java generic parameters to be of the same type

How can a similar functionality be achieved without errors?
class A<K> {
void f(K x) {}
}
void foo(A<? extends X> a, X x) {
a.f(x); // AN error: The method f(capture#1-of ? extends X) in the
// type A<capture#1-of ? extends X> is not applicable for the
// arguments (X)
}
I know that it happens because 'a' can be an instance of A<"non-X">, so its 'f' mustn't accept an instance of X as a parameter, but how can I force the parameters to be of the same type?
Here is more code:
Test class:
class Test {
<T> void foo(A<T> a, T x) {
a.f(x); // now it works!
}
}
In some class:
Container<X> container;
public void test() {
X x = new X();
new Test().foo(container.get(), x);
}
Here's the container class:
public class Container<K> {
A<? extends K> get() {
return new A<K>();
}
}
You can force the parameters to be of the same type by doing the following:
// the first class, A<K>:
class A<K> {
void f(K x) {}
}
// the second class, defining the method with generic type parameters
class Test {
<T> void foo(A<T> a, T x) {
a.f(x); // now it works!
}
}
// a third class, that uses the above two:
class Main {
public static void main(final String... args) {
final Test test = new Test();
final A<String> a = new A<>();
test.foo(a, "bar");
}
}
What this does is: the method foo defines a generic type parameter T and uses it to enforce that the K type parameter of the class A must match the type of x, the second parameter of foo.
You could even impose restrictions on <T> if you wish and if it makes sense for your problem, such as <T extends Bar> void foo(A<T> a, T x) {...}, or with super. You would want this if, as Joni asked on a comment in the question, X is actually a type and not a type parameter: you'd use <T extends X> void foo(...).
After you've shown more code, the problem becomes clear.
The method .get() of the container returns an instance of A<? extends K>. Therefore, the type parameter of the instance you obtain from .get() is not fully specified. Usually, it is not very good design to return such an unspecified type. For a video presentation with Joshua Bloch, the author of Effective Java and many APIs and features in Java, showing how to improve such an API, check this: http://www.youtube.com/watch?v=V1vQf4qyMXg&feature=youtu.be&t=22m. At exactly 25'36", Joshua Bloch says "don't try to use them [wildcard types] on return values", and he explains it later. Basically, you don't get any more flexibility by using them, and just makes it painfully hard for users of the API to deal with it (you just felt the effects of doing it...).
To fix, you could simply try to change the signature of .get() to A<K> get(), so the container class would be:
public class Container<K> {
A<K> get() {
return new A<K>();
}
}
Since you do know that get() is returning an instance of A<K>, there's no reason to use the older signature: it simply makes you lose information you already know!
And if this still doesn't work, your problem might be somewhere else, and you'd need to show even more code... or better still, ask other questions! :)
Keeping in mind the PECS rule, and given the way you are using X, you should be specifying as a lower instead of upper bound:
void foo(A<? super X> a, X x)
This way no compiler errors are produced, and you have the most general signature applicable.

Why can't I declare a parameterized static class variable?

Trying to create a static field with a generic type doesn't compile:
class MyClass {
public static Function<Z, Z> blargh = new Function<Z, Z>() {
public Z apply(Z a) {
return a;
}
};
}
Eclipse says:
Multiple markers at this line
- Z cannot be resolved to a type
- Z cannot be resolved to a type
- Z cannot be resolved to a type
- Z cannot be resolved to a type
- The type new Function<Z,Z>(){} must implement the inherited
abstract method Function<Z,Z>.apply(Z)
but replacing all the Zs with a concrete type works just fine:
static Function<Integer, Integer> blargh = new Function<Integer, Integer>() {
public Integer apply(Integer a) {
return a;
}
};
What's going on here?
Context:
I was originally trying to figure out why this code uses a method instead of a field:
public static <T extends Throwable> F<T, String> eMessage() {
return new F<T, String>() {
public String f(final Throwable t) {
return t.getMessage();
}
};
}
Maybe it's to overcome this restriction?
the Function type is from Google's guava library.
Edit: Now I see the problem better.
I think that firstly you would have to declare the type as a class parameter:
class MyClass<Z> {
to get visibility, but now the reason you can't use it like that is because the static member should be shared among all the instances of the class. But since you could create instances with different type parameters, the static member depending on a particular type would not make sense.
You can only use class-level generics on member fields. For example:
public class MyClass<Z> {
private Function<Z, Z> function;
// ...
}
is correct. Declaring this static instead will break. Why?
Think about ArrayList. Its class declaration is something like:
public class ArrayList<E> extends AbstractList<E> implements List<E>, ... {
// ...
}
E has no context in a static sense, because static variables belong to all instances of ArrayList, but E can be different for each ArrayList instance:
// Here's one ArrayList with E as String
List<String> strs = new ArrayList<String>();
// And another with E as Boolean
List<Boolean> bools = new ArrayList<Boolean>();
So because E can change from instance to instance, it doesn't make sense to have an E variable at the static level.
Now you can declare static methods with generics, but in a totally different way. For example, Collections.sort could have a declaration like this:
public static <T> void sort(List<? extends T> list, Comparator<T> comparator)
Notice that T is declared as part of the method before the return type. This is defining the context of T within the method, and T can differ from call to call.
Post-edit remark: in your case, you don't have Z declared anywhere, so you won't be able to use it anyway. See my declaration above for MyClass. Notice how I used <Z> directly on the class? That means that Z will be some arbitrary type.
In the case of what you were trying to figure out, you should look at Function as a generic way of representing a transformation. Let's dissect your posted method:
public static <T extends Throwable> F<T, String> eMessage() {
return new F<T, String>() {
public String f(final Throwable t) {
return t.getMessage();
}
};
}
First, note that this is a method, not a static field like your OP, so it's legal to have generics here. Also, it's static, so any generics need to be declared before the return type. Here, they declare <T extends Throwable>, so T must be some kind of error or exception that extends Throwable. The return type is F<T, String>, which is a function that takes a T (a Throwable) and returns a String. The actual object declares an f method which does just that by calling Throwable.getMessage. Since the project is functionaljava, everything is based on the F class, so generics are everywhere.
Just remember:
Generics declared at the class level can only be used by non-static members and methods.
Generics declared at the method level are allowable, but don't refer to the class-level types, referring instead to types declared before the return type.
Generics declared at the static field level simply aren't allowed because they'll never have context for their concrete type.
I think the simplest answer might be that: although the JDK compiler is flexible in how it interprets generics, it is impossible to modify or specify the "Z" class given the semantics of your code.
In all use of generics, you must define a syntax which specifies the identity of the generic class that is being operated upon. For example (As in the examples above).
1) Use a generic, parameterized utility function. In this case, its obvious to the compiler because the specified class is sent as input the function.
2) Define the class itself as being generic, and non static. This would then require that the user of the class declare it with the proper specified class parameter.
Specifically, for Function classes, you are clearly defining a constrained class : one which takes "Z" as input, and returns "Z" as output. If you want to generify this, you might create a FunctionFactory class, which takes in, for example, a single instance of Z, and returns a type-specified function of type :
public static <Z> Function<Z,Z> functionFactory(final Z default){
return new Function<Z,Z>(){
#Override
public Z apply(Z input) {
// TODO Auto-generated method stub
if(input==null)
return default;
else
return input;
}
};
}

Java passing generic class with parameterzied enum as parameter to another generic object

I am trying to implement parameterized class in Java with enum as a type parameter. Everything works fine, except, if you look at the code below, there is that anonymous class Car.Creator with parameter K. But of course, instead of K, there should be CarObject<T>, but it's not that easy. If I put CarObject<T> in K's place, then I got syntax error. Could someone explain if something like this is possible and maybe provide with some code sample.
public class CarObject<T extends Enum<T>>
{
public Map<T, String> values;
private Class<T> typeClass;
public CarObject(Class<T> clazz)
{
typeClass = clazz;
values = new EnumMap<T,String>(typeClass);
}
public static final Car.Creator<K> Creator = new Car.Creator<K>()
{
public K create()
{
return new K();
}
public K[] newArray(int size)
{
return new K[size];
}
}
}
I can give you an example from official Android documentation(look at the code in 'Class Overview') where this works perfectly fine. I think there is some magic going on under the hood in Android. I'm trying to do exactly the same thing - implement Parcelable interface. I just made up this example without implements Parcelable and other stuff, because I though maybe it's just a syntax sugar :).
Look at the following statement -
public static final Car.Creator<K> Creator = new Car.Creator<K>() {
public K create() {
return new K();
}
public K[] newArray(int size) {
return new K[size];
}
}; // <------ missing a semicolon here
Looks like you have a generic class Creator defined as static inner class in the class Car. And, here your trying to use it and instantiate it, so can not use K here unless it's a class or an interface defined somewhere.
I think the following example explains what I mean -
// you are not defining a class here so no parameter instead use a real class
public static final List<K> list = new List<K>() { /*....*/ };
Make the method non-static as per the compiler message, but you have several other major design problems anyway. create() needs a clazz from somewhere, and you can't return generic arrays.
EDIT I don't normally post code solutions but you seem pretty confused.
public class CarObject<T extends Enum<T>>
{
public Map<T, String> values;
private Class<T> typeClass;
public CarObject(Class<T> clazz)
{
typeClass = clazz;
values = new EnumMap<T, String>(typeClass);
}
public final Car.Creator<CarObject<T>> Creator = new Car.Creator<CarObject<T>>()
{
#Override
public CarObject<T> create()
{
return new CarObject<T>(typeClass);
}
#Override
public CarObject<T>[] newArray(int size)
{
return new CarObject[size];
}
};
}
I'm no Android expert, but I'm pretty sure there's nothing magic about how this is happening in Android. I think you're just confused about the relationship between generic type arguments and statics.
Generic type arguments (like T in your example) belong to the instances of the class, not to the class itself.
Static members (like Creator in your example) belong to the class itself, not to the instances of the class.
The fact that Creator is an anonymous inner class implementing a generic interface is a bit of a red herring here. The issue is simply that, regardless of what Creator is, as long as it is static, it can't access the type T.

Categories