Reputation: 680
Here the small piece of code and I don't understand why the javac can't compile it. What I miss? Is there are any errors?
public class HelloWorld<T> {
private static enum Type {
}
private T value;
private List<Type> types = new ArrayList<>();
public T getValue() { return value; }
public List<Type> getTypes() { return types; }
public static void main( String[] args ) {
for ( Type type : new HelloWorld().getTypes() ) { // Error: Type mismatch: cannot convert from element type Object to HelloWorld.Type
}
}
}
Why is getTypes()
returning an Object
(raw) list when it is should Type
list?
Upvotes: 3
Views: 166
Reputation: 3171
While experimenting with your code I noticed something very interesting. In order to remove the compiler error:
Error: Type mismatch: cannot convert from element type Object to HelloWorld.Type
Since it is stating the returned element is of type Object
I decided to type cast it to List<Type>
as shown:
public static void main( String[] args ) {
for ( Type type : (List<Type>)new HelloWorld().getTypes() ) {
}
}
This compiled successfully with warning so I used the -Xlint
with javac
to see what is the warning and I found the following:
HelloWorld.java:15: warning: [rawtypes] found raw type: HelloWorld
for ( Type type : (List<Type>)new HelloWorld().getTypes() ) {
^
missing type arguments for generic class HelloWorld<T>
where T is a type-variable:
T extends Object declared in class HelloWorld
HelloWorld.java:15: warning: [unchecked] unchecked cast
for ( Type type : (List<Type>)new HelloWorld().getTypes() ) {
^
required: List<Type>
found: List
2 warnings
Here I was astonished to see the second warning. It states that required is List<Type>
but found List
a RAW TYPE. So it means that if you initialise a raw type object and call a method that returns a variable having generics, this variable will also be converted to RAW TYPE. In order to test this I implemented a class HelloWorldTest
as :
public class HelloWorldTest<T>{
private T t;
public HelloWorldTest(T t){
this.t = t;
}
public T getT(){
return t;
}
}
Then I changed your code to test the condition as :
public class HelloWorld<T> {
private HelloWorldTest<Integer> test = new HelloWorldTest<>(1);
public HelloWorldTest<Integer> getTest(){
return test;
}
public static void main( String[] args ) {
HelloWorldTest<Integer> hello = new HelloWorld().getTest();
}
}
This compiles successfully but with warnings so using -Xlint
switch to compile I get the following Warnings:
HelloWorld.java:10: warning: [rawtypes] found raw type: HelloWorld
HelloWorldTest<Integer> hello = new HelloWorld().getTest();
^
missing type arguments for generic class HelloWorld<T>
where T is a type-variable:
T extends Object declared in class HelloWorld
HelloWorld.java:10: warning: [unchecked] unchecked conversion
HelloWorldTest<Integer> hello = new HelloWorld().getTest();
^
required: HelloWorldTest<Integer>
found: HelloWorldTest
2 warnings
So, Here also we find that the HelloWorldTest
has been converted to a raw type.
Finally we can infer that : If you initialise a raw type object and call a method that returns a variable having generics, this variable will also be converted to RAW TYPE.
Now when I replaced
HelloWorldTest<Integer> hello = new HelloWorld().getTest();
with
Integer hello = new HelloWorld().getTest().getT();
As expected I got the error :
HelloWorld.java:10: error: incompatible types: Object cannot be converted to Integer
Integer hello = new HelloWorld().getTest().getT();
^
1 error
Finally if you replace the main method in my implementation of HelloWorld
class with:
public static void main( String[] args ) {
String hello = (String) new HelloWorld().getTest().getT();
}
It compiles successfully with only warning being:
HelloWorld.java:10: warning: [rawtypes] found raw type: HelloWorld
String hello = (String) new HelloWorld().getTest().getT();
^
missing type arguments for generic class HelloWorld<T>
where T is a type-variable:
T extends Object declared in class HelloWorld
1 warning
This is quite misleading as this will definitely run into runtime errors and again illustrates the dangers of RAW TYPE in generics.
Upvotes: 1
Reputation: 492
change for
loop to add a generic type for the class:
for e.g: I have just used 'Type
' as an example here.
for ( Type type : new HelloWorld<Type>().getTypes() ) {
Edit :
It could be 'String' as well as pointed out in the comments.(thanks for that). In your case, it should be actual type which you need for that class.
The idea is that the generic type was missing which needs to be added.
Upvotes: 0
Reputation: 393811
This looks like a compiler limitation to me. getTypes
always returns a List<Type>
, so using the raw HelloWorld
type should make no difference.
That said, either of these two solutions will overcome the error :
Create a parameterized type of HelloWorld
instead of a raw type :
for (Type type : new HelloWorld<Integer>().getTypes() ) { // any type will do, I chose
// Integer arbitrarily to show
// that it doesn't matter
}
Use a local variable to store the List before using it :
List<Type> types = new HelloWorld().getTypes();
for (Type type : types) {
}
Anyway, parameterized types should always be preferred over raw types, so I'll use the first solution (with whatever type parameter makes sense in your class).
Upvotes: 3
Reputation: 1461
Within main
method:
for ( Type type : new HelloWorld().getTypes() ) {//Here also you did not mention the type for the new object since
}
Try this in main method:
for ( Type type : new HelloWorld<Type>().getTypes() ) {
}
Upvotes: 0