Mirian
Mirian

Reputation: 680

Why this piece of code isn't compilable

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?

Link to online compiler

Upvotes: 3

Views: 166

Answers (4)

Blip
Blip

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

anish
anish

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

Eran
Eran

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 :

  1. 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
    
    }
    
  2. 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

Estimate
Estimate

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

Related Questions