Why is using Collection<String>.class illegal? - java

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.

Related

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.

Discrepancy in Generic parametric polymorphism

public class Foo<T extends Bar>{
private Class<T> _type;
public Foo( Class<T> _type ){
this._type = _type;
}
public Collection<T> hypothetical( List<T> items ){ //PROBLEMATIC
return dostuffWithItems( items );
}
}
Usage:
Foo<? extends ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
List<ChildBar> items = ( List<ChildBar> ) foo.hypothetical( new ArrayList<ChildBar>() ); //COMPILER ERROR: The method hypothetical(List<capture#2-of ?>) in the type Foo<capture#2-of ?> is not applicable for the arguments (List<ChildBar>)
The compiler would either accept
casting List<ChildBar> items argument to List<?>
or changing the hypothetical( List<T> items ) signature to either
a) hypothetical( List<ChildBar> items ) or
b) hypothetical( List<? extends Bar> items )
However, none of the alternatives assure that the hypothetical method's List items argument T type is the equivalent runtime type of the Foo class T parametric type. I am currently using an extra method to verify the parametric types at the moment.
Is there a better way within Java generics constructs to achieve this automatically without the extra logic? Or better yet, why can I not declare foo as Foo<? extends Bar> and then fill in the actual type parameter at runtime?
I edited your code and added the missing stuff to make it compilable, and I can confirm that the only problematic parts are:
The missing dostuffWithItems method.
The typos with the hypothetical method name.
Assigning a Collection<ChildBar> to a List<ChildBar>.
The first two are easy to fix.
The last one requires you to either change the change the API method, or change the code where you are calling it. Neither of these is (IMO) problematic. Furthermore, the
It is worth noting that you would get all of these errors if the types were non-generic. You can't assign a Collection to a List without a typecast.
Here's my code for you to play with. (Copy and paste into appropriately named files ...)
public class Bar {
}
public class ChildBar extends Bar {
}
import java.util.*;
public class Foo<T extends Bar> {
private Class<T> _type;
public Foo( Class<T> _type ) {
this._type = _type;
}
public Collection<T> hypothetical( List<T> items ) {
return items; // dummy implementation ...
}
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Foo<ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
Collection<ChildBar> items =
foo.hypothetical( new ArrayList<ChildBar>() );
}
}
The accepted answer doesn't precisely explain why the snippet in the question (after edits) is rejected by the compiler.
We start from the observation that the snippet from #Stephen C's answer is accepted, while revision 8 of the question is rejected. The difference is: in the latter version the variable foo is declared with a wildcard-parameterized type Foo<? extends ChildBar>, while Stephen C had copied Foo<ChildBar> from an earlier revision (we all seem to agree that this is a suitable way to resolve the compile error).
To understand why this difference is crucial please see that with Foo<? extends ChildBar> foo this wildcard propagates as a capture into the signature for the invocation of foo.hypothetical, so this invocation is rendered as hypothetical(List<capture#2-of ?>), meaning that the parameter has an unknown (upper-bounded) type parameter. List<ChildBar> is not compatible to that type, hence the compile error.
Also note that all mentions of "runtime" in this thread are inappropriate, all this is statically resolved at compile time. Perhaps you meant invocation type, or type of the actual argument, as opposed to the declared type (of the formal parameter). The actual runtime type is unknown to the compiler.
This seems to be currently impossible in Java.
Foo<? extends ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
This leave foo with an ambiguous parametric type. It is obvious that ChildBar would become the true de facto parametric type. The call to the foo.hypothetical() method with the List<ChildBar> exposes this assumption to be untrue. Although foo.hypothetical only accepts a List<> argument containing elements of the foo parametric type, it still fails to recognize that the argument was a list of ChildBar objects.
For this use case, the object parametric type must be specified during foo declaration in order make it a part and parcel of the foo runtime reference.
Foo<ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
All conforming List<ChildBar> arguments of the foo.hypothetical method will now correctly be accepted as carrying elements of the foo's declared parametric type.

Usage of Java generics when the type is known only at runtime

Consider the following code:
public class Generics {
C c; // initialized at runtime
public void testGenericsCall(Object o) {
c.myMethod(o);
}
}
class C<E> {
public void myMethod(E input) {
}
}
This is working, but I get warnings because the parametrized class C is used with a raw type. I cannot use a declaration like
C<String> c;
because the type of C is known only at runtime. I also cannot add a type parameter to the class Generics because I need to create objects of this class before knowing the type of C. The declaration
C<?> c;
or
C<? extends Object> c;
would be OK for the compiler, but then the method testGenericsCall does not compile ("actual argument java.lang.Object cannot be converted to capture#1 of ? by method invocation conversion")
What is the best way to deal with a situation like this?
EDIT: Note that when I actually (at runtime) create an instance of C, I know its type parameter, this part of the code is type-safe and working well. In the real code, I don't have a single "C" class, but a series of interrelated classes, and there the generics are definitely useful (even if in this simplified example this is not obvious - so please don't just tell me not to use generics :). I already have the compile-time type-safety, but not here, but between C and other classes (not shown here).
I see how in this case I cannot check the type parameter at compile time, that's why I tried to declare it C<?> c. Here I am just looking for the best way to bridge the generic and not-generic code without compiler warnings.
Because of type erasure, there's no way to use generics at runtime. You'll have to deal with your data type programmatically, by checking type or anything (reflection maybe).
You can do it. But through dirty tricks and reflection. Look at below code for example. Courtesy here:
class ParameterizedTest<T> {
/**
* #return the type parameter to our generic base class
*/
#SuppressWarnings("unchecked")
protected final Class<T> determineTypeParameter() {
Class<?> specificClass = this.getClass();
Type genericSuperclass = specificClass.getGenericSuperclass();
while (!(genericSuperclass instanceof ParameterizedType) && specificClass != ParameterizedTest.class) {
specificClass = specificClass.getSuperclass();
genericSuperclass = specificClass.getGenericSuperclass();
}
final ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
final Type firstTypeParameter = parameterizedType.getActualTypeArguments()[0];
return (Class<T>) firstTypeParameter;
}
}
//change the type of PrameterizedTest<Integer> to Parameterized<String> or something to display different output
public class Test extends ParameterizedTest<Integer>{
public static void main(String... args){
Test test = new Test();
System.out.println(test.determineTypeParameter());
}
}
Here on the runtime, you get the Type Parameter. So instead in your class, you will have to define a Class object which gets the class as explained above. Then using Class.newInstance you get a new Object. But you will have to manually handle type cast and so on.
The question is: Is all this worth it??
No according to me as most of it can be avoided by using bounds in generic types and interfacing to the bound type

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

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.

Why doesn't Class have a nice generic type in this case?

In this code, why can type not be declared as Class<? extends B>?
public class Foo<B> {
public void doSomething(B argument) {
Class<? extends Object> type = argument.getClass();
}
}
This problem is that Java's syntax doesn't allow getClass() to say it's returning a type which matches the class its defined in, and this is not a special case as far as the compiler is concerned. So you are forced to cast the result.
There are many cases where you would like to be able to specify this type, e.g. for chaining, so I would hope they include this feature one day.
You could write
Class<? extends this> getClass();
or
public this clone(); // must return a type of this class.
or
class ByteBuffer {
this order(ByteOrder order);
}
class MappedByteBuffer extends ByteBuffer {
}
// currently this won't work as ByteBuffer defines order()
MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, fc.size())
.order(ByteOrder.nativeOrder());
This is all about generic type erasure. From here:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods. [at compile time]
So you cannot get Class of actual type of B but only ? or ? extends Object.
If your bounds will be turn into <B extends SomeClass> instead of <B> only then you can fetch Class object of type <? extends SomeClass>.
Object.getClass() is defined to return a Class, where T is the statically known type of the receiver (the object getClass() is called on). Take special note of the vertical bars, the erasure operator. The erasure of a type variable is the erasure of its leftmost bound. In your case that's the implicit bound Object. So you get back a Class, not a Class<? extends T>.
The right way to do it is,
abstract class AbstractExecutor<E> {
public void execute() throws Exception {
List<E> list = new ArrayList<E>();
Class<E> cls = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
E e = cls.getConstructor(String.class).newInstance("Gate");
list.add(e);
System.out.println(format(list));
}
// ...
}
Because the class of a given object is not guaranteed to be the same as the type it is stored in.
eg.
Object o = "some string";
Class<Object> clazz = o.getClass(); // actually Class<String>
By looking at the type you should expect the Class for Object, but in reality you get the class for String. What problem is this you might ask -- Object is a superclass of String, so String implements all the methods implemented by Object.
Problems
The problems are that when getting a Field the class will return the fields of the actual class and not the generic type. In addition, whilst Method is able to invoke the correct method if there is an overriding method in the given object, it is not able to do the reverse and find an implementation of the method that will work on the given object.
For instance, Object declares hashCode, so all objects have a hash code method. However, the following will produce a runtime exception:
Object.class.getMethod("hashCode").invoke("some string"); // works
String.class.getMethod("hashCode").invoke(new Object()); // fails
This is because the Method object for hashCode is expecting a String. It's expecting to generate a hash code from the sequence of characters, but the provided object does not have a char array for the method to work on, so it fails.
Meaning the following looks like it should work, but won't because the actual method returned by getMethod is the hash code method for String.
Object obj = "string";
Class<Object> clazz = obj.getClass();
clazz.getMethod("hashCode").invoke("another string");

Categories

Resources