What is causing this compile error with java Generics and reflection? - java

I have a Generic method that should work similarly to recursion, but calling different instances of the method for each calling.
public <M extends A> void doSomething(Class<M> mClass, M mObject)
{
// ... Do something with mObject.
A object = getObject();
Class<? extends A> objectClass = object.getClass();
doSomething(objectClass, objectClass.cast(object)); // Does not compile.
}
private A getObject() {...}
The problem is the line with a comment does not compile, giving the following error:
The method doSomething(Class, M) in the type MainTest is not applicable for the arguments (Class, capture#3-of ? extends A)
I don't quite understand why the compiler does not compile if it can call doSomething with M = "? extends A".
Why doesn't it compile?

Ok here is a crude explanation
You've typed your method so that it will accept M which is a subtype of A
Now you are calling your method using 'objectClass' which is a subtype of A BUT not necessarily a subtype of M.
Hence the compiler is complaining...
If you can explain what you are trying to do a bit more, I can help with a solution.

The language does not track wildcards like that (it seems). What you need to do is to capture that wildcard, which can be done with a method call with type inference.
public <M extends A> void doSomething(Class<M> mClass, M mObject) {
// ... Do something with mObject.
A object = getObject();
Class<? extends A> objectClass = object.getClass();
privateSomething(objectClass, object);
}
private <T extends A> void privateSomething(Class<T> objectClass, A object) {
doSomething(objectClass, objectClass.cast(object)); // Should compile.
}
As always, whilst reflection has some uses, it's usually a sign of confusion.

When you are asking the compiler to perform a cast, the exact type to perform the cast must be known. It is not sufficient to tell the compiler that you don't know about the exact type excerpt that it's a subclass of A.
Class tell the compiler that the type of the object is a subclass of A but it doesn't tell the compilator the exact type to be used for the casting.
Your problem is that you are trying to replace Polymorphism with Generic. As you are learning the hard way, Generic is not the new modern way of doing Polymorphism.

Related

How do I make a generic method when the collection<T> needs genericizing?

Suppose I have two methods:
public Set<String> method1()
public List<String> method2()
How do I make a generic method off this? Specifically, I'm looking to genericize the "Set" and "List".
Here's an attempt that didn't work:
public static <T extends Collection> T<String> genericMethod
It's showing a compiler error: Type "T" does not have type parameters.
As far as the signature goes, it would be
public static <T extends Collection<String>> T genericMethod() {
...
}
Presumably, genericMethod is going to create an instance of T at some point and return that, rather than just returning null (that wouldn't be very useful, would it?), but there is no guarantee that T has any constructors at all. And due to type erasure, the runtime wouldn't know what type to create anyway. To work around this, the method would also need to accept a parameter that tells it how to create a T:
public static <T extends Collection<String>> T genericMethod(Supplier<? extends T> tSupplier) {
...
}
Now, rather than saying new T(), which is invalid, you do tSupplier.get() to get a T.
If the caller wants a Set<String>, for example, they would do:
Set<String> set = genericMethod(HashSet::new);
Note that the specific implementation of the collection is now specified by the caller, rather than hidden as an implementation detail of genericMethod. This is inevitable, as the specific type of collection (T) is now unknown to genericMethod.

How Automatic Type Inference works if the constructor parameter is not generic

Case 1:
class Gen3<T extends Number> {
T val;
<T extends String> Gen3(String ob){
}
}
Here compiler doesn't give any error, but it should give right? Because here are two contradicting bounds for T. Please help me in understanding this.
Case 2:
class Gen3<T extends Number> {
T val;
<T extends String> Gen3(String ob) {
}
}
Suppose if I write following to test the above class
Gen<Integer> a = new Gen<>("r");
Now how automatic type inference would work here?
Please help in understanding this.
There are no two contradicting bounds of T. There are two type variables that happen to have the same name. In the constructor, the type parameter T hides the type parameter of the class level.
Note that the issue is not with the different type bounds. If you actually try to do something with the type parameter, such as:
class Gen3<T extends Number> {
T val;
<T extends Number> Gen3(T ob) {
val = ob;
}
}
This won't pass compilation even if both Ts have the same type bound, since the type parameter of ob is different than the type parameter of val.
"Because here are two contradicting bounds for T" - no. There simply a two separate definitions of T that have nothing to with each other. The T on the constructor hides the T of the class, same with local variables vs. fields of the same name. A "proper" IDE will tell you that the inner T hides the outer T and is unused.
This "use case" of Generics doesn't make sense in multiple aspects. With the <T extends String> clause you introduce a type variable that you don't use and don't give the compiler a chance to replace it with a concrete type in a given call situation.
Your definition is equivalent to the following (I just renamed the two different type variables to have different names, making the discussion easier):
class Gen3<T extends Number> {
T val;
<U extends String> Gen3(String ob) {
}
}
The <U extends String> clause tells the compiler: "The following constructor will use a type parameter U, and I only allow U to be String or a subclass of String". As others already said, String is final, so U can only be String, so it isn't really a variable type, and declaring a variable type that can't vary doesn't make sense. I'll continue with a modified version:
class Gen3<T extends Number> {
T val;
<U extends Collection> Gen3(String ob) {
}
}
If you do Gen<Integer> a=new Gen<Integer>("r");, how should the compiler find out the concrete class to replace U with? The <Integer> part applies to the T variable, so it doesn't help for U. As you don't refer to U in any of the arguments, there's no hint for the compiler.
The idea of Generics is that a class has some elements where you want to allow for varying types, and allow the compiler to flag misuse, e.g. add an Integer to a List<String>:
List<String> myList = new ArrayList<String>();
myList.add(new Integer(12345));
Here, the compiler can match the generic List<E> type parameter E to be a String (from the List<String> declaration). In this context, the gegeric List.add(E e) method declaration becomes an add(String e), and doesn't match the usage with new Integer(12345), which isn't a String, allowing the compiler to flag the error.
Summary:
Introduce a type parameter only if you give the compiler a chance to deduce it from the call arguments.

Java Generics - method not applicable to Mockito generated stub

I've got a ThingsProvider interface I'm trying to test with Mockito, (simplified version) defined as follows:
interface ThingsProvider {
Iterable<? extends Thing> getThings()
}
Now when I'm going to test it with Mockito, I'm doing the following (again, simplified for the question):
ThingsProvider thingsProvider = mock(ThingsProvider.class);
List<Thing> things = Arrays.asList(mock(Thing.class));
when(thingsProvider.getThings()).thenReturn(things); // PROBLEM IS HERE
Compile error message: The method thenReturn(Iterable<capture#11-of ? extends Thing>) in the type OngoingStubbing<Iterable<capture#11-of ? extends Thing>> is not applicable for the arguments (List<Thing>)
Now, purely for getting the test going, I'm changing the last line to
when(thingsProvider.getThings()).thenReturn((List)things); // HAHA take THAT generics!
... but this would clearly be bad to do in non-testing code.
My question(s):
Why is this an error? I'm clearly returning objects that extend Thing, which is what the interface expects.
Is there a better way of solving this? Perhaps defining my interface differently? I haven't run into this issue outside of testing so far...
On #2 - The main reason I'm not simply returning Iterable<Thing> is that there are several different extensions where the concrete types have things buried in them that return specific subtypes, and I end up with issues like Type mismatch: cannot convert from Iterable<MagicalThing> to Iterable<Thing> - maybe the solution is a better way to fix this issue?
For those of you less familiar with Mockito, a more pure Java version of #1 is below:
public static void main(String...args) {
List<Integer> ints = Arrays.asList(1,2,3);
blah(ints);
Foo<Number> foo1 = new Foo<Number>();
foo1.bar(ints); // This works
Foo<? extends Number> foo2 = new Foo<Number>();
foo2.bar(ints); // NO COMPILEY!
}
private static void blah(List<? extends Number> numberList) {
// something
}
public static class Foo<T> {
public Object bar(List<? extends T> tList) {
return null;
}
}
Wildcard in return type is very convenient for subclass implementations, as you've observed.
It doesn't make much difference to the callers of the method though; you may change it to Iterable<Thing> if you'd like to; it's simpler on javadoc, at the expense of subclass implementers. Subclass can do brute cast if necessary, e.g. List<MyThing> => Iterable<Thing>, thanks to erasure.
The reason for your problem is wildcard capture; basically every expression goes though wildcard capture first before the contextual expression is evaluated. In a method invocation
foo( arg )
arg is always wildcard-captured first, before method applicability/overloading/inference are done. In your case, the more general type Iterable<? extends Thing> is lost, becomes Iterable<CAP#>.
Usually, wildcard capture does not pose any problem; but Mockito semantics is nothing usual.
The first solution is to avoid type inference; explicitly supply type arguments instead
Mockito.<Iterable<? extends Thing>>when(...
Or as Delimanolis suggested, use a target type to restrict inference (in java8+)
OngoingStubbing<Iterable<? extends Thing>> stub = when(...
It also appears that lambda inference may be helpful for this case
static <T> OngoingStubbing<T> whenX( Supplier<T> sup )
{
return Mockito.when( sup.get() );
}
whenX(thingsProvider::getThings).thenReturn(things);
// T = Iterable<? extends Thing>
And - if your interface is simple enough, just directly implement it instead of mock it :)
List<Thing> things = ...;
ThingsProvider thingsProvider = ()->things;
Refactor into two steps
OngoingStubbing<Iterable<? extends Thing>> stub = when(thingsProvider.getThings());
stub.thenReturn(things);
or use what bayou.io suggested
Mockito.<Iterable<? extends Thing>>when(thingsProvider.getThings())

Generic method with parameters vs. non-generic method with wildcards

According to this entry in the Java Generics FAQ, there are some circumstances where a generic method has no equivalent non-generic method that uses wildcard types. According to that answer,
If a method signature uses multi-level wildcard types then there is always a difference between the generic method signature and the wildcard version of it.
They give the example of a method <T> void print1( List <Box<T>> list), which "requires a list of boxes of the same type." The wildcard version, void print2( List <Box<?>> list), "accepts a heterogenous list of boxes of different types," and thus is not equivalent.
How do you interpret the the differences between the following two method signatures:
<T extends Iterable<?>> void f(Class<T> x) {}
void g(Class<? extends Iterable<?>> x) {}
Intuitively, it seems like these definitions should be equivalent. However, the call f(ArrayList.class) compiles using the first method, but the call g(ArrayList.class) using the second method results in a compile-time error:
g(java.lang.Class<? extends java.lang.Iterable<?>>) in Test
cannot be applied to (java.lang.Class<java.util.ArrayList>)
Interestingly, both functions can be called with each others' arguments, because the following compiles:
class Test {
<T extends Iterable<?>> void f(Class<T> x) {
g(x);
}
void g(Class<? extends Iterable<?>> x) {
f(x);
}
}
Using javap -verbose Test, I can see that f() has the generic signature
<T::Ljava/lang/Iterable<*>;>(Ljava/lang/Class<TT;>;)V;
and g() has the generic signature
(Ljava/lang/Class<+Ljava/lang/Iterable<*>;>;)V;
What explains this behavior? How should I interpret the differences between these methods' signatures?
Well, going by the spec, neither invocation is legal. But why does the first one type check while the second does not?
The difference is in how the methods are checked for applicability (see §15.12.2 and §15.12.2.2 in particular).
For simple, non-generic g to be applicable, the argument Class<ArrayList> would need to be a subtype of Class<? extends Iterable<?>>. That means ? extends Iterable<?> needs to contain ArrayList, written ArrayList <= ? extends Iterable<?>. Rules 4 and 1 can be applied transitively, so that ArrayList needs to be a subtype of Iterable<?>.
Going by §4.10.2 any parameterization C<...> is a (direct) subtype of the raw type C. So ArrayList<?> is a subtype of ArrayList, but not the other way around. Transitively, ArrayList is not a subtype of Iterable<?>.
Thus g is not applicable.
f is generic, for simplicity let us assume the type argument ArrayList is explicitly specified. To test f for applicability, Class<ArrayList> needs to be a subtype of Class<T> [T=ArrayList] = Class<ArrayList>. Since subtyping is reflexisve, that is true.
Also for f to be applicable, the type argument needs to be within its bounds. It is not because, as we've shown above, ArrayList is not a subtype of Iterable<?>.
So why does it compile anyways?
It's a bug. Following a bug report and subsequent fix the JDT compiler explicitly rules out the first case (type argument containment). The second case is still happily ignored, because the JDT considers ArrayList to be a subtype of Iterable<?> (TypeBinding.isCompatibleWith(TypeBinding)).
I don't know why javac behaves the same, but I assume for similar reasons. You will notice that javac does not issue an unchecked warning when assigning a raw ArrayList to an Iterable<?> either.
If the type parameter were a wildcard-parameterized type, then the problem does not occur:
Class<ArrayList<?>> foo = null;
f(foo);
g(foo);
I think this is almost certainly a weird case arising out of the fact that the type of the class literal is Class<ArrayList>, and so the type parameter in this case (ArrayList) is a raw type, and the subtyping relationship between raw ArrayList and wildcard-parameterized ArrayList<?> is complicated.
I haven't read the language specification closely, so I'm not exactly sure why the subtyping works in the explicit type parameter case but not in the wildcard case. It could also very well be a bug.
Guess: The thing representing the first ? (ArrayList) does not 'implement' ArrayList<E> (by virtue of the double nested wildcard). I know this sounds funny but....
Consider (for the original listing):
void g(Class<? extends Iterable<Object> x) {} // Fail
void g(Class<? extends Iterable<?> x) {} // Fail
void g(Class<? extends Iterable x) {} // OK
And
// Compiles
public class Test{
<T extends Iterable<?>> void f(ArrayList<T> x) {}
void g(ArrayList<? extends Iterable<?>> x) {}
void d(){
ArrayList<ArrayList<Integer>> d = new ArrayList<ArrayList<Integer>>();
f(d);
g(d);
}
}
This
// Does not compile on g(d)
public class Test{
<T extends Iterable<?>> void f(ArrayList<T> x) {}
void g(ArrayList<? extends Iterable<?>> x) {}
void d(){
ArrayList<ArrayList> d = new ArrayList<ArrayList>();
f(d);
g(d);
}
}
These are not quite the same:
<T extends Iterable<?>> void f(Class<T> x) {}
void g(Class<? extends Iterable<?>> x) {}
The difference is that g accepts a "Class of unknown that implements Iterable of unknown", but ArrayList<T> is constrained implementing Iterable<T>, not Iterable<?>, so it doesn't match.
To make it clearer, g will accept Foo implements Iterable<?>, but not AraryList<T> implements Iterable<T>.

Why is using Collection<String>.class illegal?

I am puzzled by generics. You can declare a field like:
Class<Collection<String>> clazz = ...
It seems logical that you could assign this field with:
Class<Collection<String>> clazz = Collection<String>.class;
However, this generates an error:
Syntax error on token ">", void expected after this token
So it looks like the .class operator does not work with generics. So I tried:
class A<S> { }
class B extends A<String> { }
Class<A<String>> c = B.class;
Also does not work, generates:
Type mismatch: cannot convert from Class<Test.StringCollection> to Class<Collection<String>>
Now, I really fail to see why this should not work. I know generic types are not reified, but in both cases it seems to be fully type safe without having access to runtime generic types. Anybody an idea?
Generics are invariant.
Object o = "someString"; // FINE!
Class<Object> klazz = String.class; // DOESN'T COMPILE!
// cannot convert from Class<String> to Class<Object>
Depending on what it is that you need, you may be able to use wildcards.
Class<? extends Number> klazz = Integer.class; // FINE!
Or perhaps you need something like this:
Class<List<String>> klazz =
(Class<List<String>>) new ArrayList<String>().getClass();
// WARNING! Type safety: Unchecked cast from
// Class<capture#1-of ? extends ArrayList> to Class<List<String>>
As for the non-reified at run-time case, you seem to have a good grasp, but here's a quote anyway, from the Java Tutorials on Generics, The Fine Print: A Generic Class is Shared by All Its Invocations:
What does the following code fragment print?
List <String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());
You might be tempted to say false, but you'd be wrong. It prints true, because all instances of a generic class have the same run-time class, regardless of their actual type parameters.
That is, there's no such thing as List<String>.class or List<Integer>.class; there's only List.class.
This is also reflected in the JLS 15.8.2 Class Literals
A class literal is an expression consisting of the name of a class, interface, array, or primitive type, or the pseudo-type void, followed by a . and the token class.
Note the omission of any allowance for generic type parameters/arguments. Furthermore,
It is a compile time error if any of the following occur:
The named type is a type variable or a parameterized type, or an array whose element type is a type variable or parameterized type.
That is, this also doesn't compile:
void <T> test() {
Class<?> klazz = T.class; // DOESN'T COMPILE!
// Illegal class literal for the type parameter T
}
Basically you can't use generics with class literals, because it just doesn't make sense: they're non-reified.
I agree with the other answers, and would like to explain one point further:
Class objects represent classes that are loaded into the JVM memory. Each class object is actually an in-memory instance of a .class file. Java generics are not separate classes. They are just a part of the compile-time type-checking mechanism. Therefore, they have no run-time representation in a class object.
There seems to be a lack in class literals in Java, there is no way to create class literals with generic information while this can be useful in certain cases. Therefore, the following code cannot be called because it is impossible to provide the class literal
class A<S> {}
<S> A<S> foo( Class<A<S>> clazz ) {}
A<String> a = foo( A<String>.class ) // error
However, my main problem was I could also not call it with a class B that extended A. This was caused by the invariance restrictions. This was solved by using a wildcard:
class A<S> {}
class B extends A<String> {}
<S> A<S> foo( Class<? extends A<S>> clazz ) { return null; }
void test () {
A<String> s = foo( B.class );
}
That said I have not found a reason what the underlying reason is that Class<A<S>>.class is invalid. Neither erasure nor bounds seem require that this is invalid.

Categories

Resources