Why is this piece of code not compiling
There is a small piece of code here and I don't understand why javac cannot compile it. What do I miss? Are there any mistakes?
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 does it getTypes()
return a Object
(raw) list when it should Type
list?
source to share
This looks like a compiler limitation to me. getTypes
always returns List<Type>
, so using raw HelloWorld
is irrelevant.
However, either of these two solutions will overcome the error:
-
Create a parameterized type
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) { }
In any case, 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).
source to share
While experimenting with your code, I noticed something very interesting. To remove the compiler error:
Error: Type Mismatch: Cannot Convert from Object Type to HelloWorld.Type
Since it states that the returned item is of type Object
, I decided to inject it in List<Type>
as shown:
public static void main( String[] args ) {
for ( Type type : (List<Type>)new HelloWorld().getTypes() ) {
}
}
This compiled successfully with a warning, so I used -Xlint
with javac
to find out what the warning is and I found this:
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 amazed to see the second warning. It states what is required List<Type>
but found List
a RAW TYPE . So this means that if you initialize an object of a raw type and call a method that returns a generic variable, that variable will also be converted to RAW TYPE . To test this, I implemented the 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 my code to check the condition like this:
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 the switch -Xlint
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 it has HelloWorldTest
been converted to a raw type.
Finally, we can conclude that: If you initialize a raw object and call a method that returns a generic variable, that variable will also be converted to RAW format .
Now that I have replaced
HelloWorldTest<Integer> hello = new HelloWorld().getTest();
from
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 class implementation with the HelloWorld
following:
public static void main( String[] args ) {
String hello = (String) new HelloWorld().getTest().getT();
}
It compiles successfully with a warning:
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 it will definitely fail at runtime and again illustrates the dangers of RAW TYPE in generics.
source to share
modify the for
loop to add a generic type for the class:
for example, for example: "I used" Type
"as an example.
for ( Type type : new HelloWorld<Type>().getTypes() ) {
Edit:
It can be "String" and is also indicated in the comments (thanks for that). In your case, this should be the actual type you need for this class.
The idea is that there was no generic type to add.
source to share