1

I've encountered a strange case of a Java program that doesn't compile and I really don't understand why.

The code can be seen below - note that we have an innerclass parameterized by T but it doesn't use the type parameter (it contains just one field that only involves specific types). The only part that fails to compile is this:

    TestClass et2 = new TestClass();
    for(String s : et2.columns) {
        System.out.println("Joy!");
    }

Specifically the line with the "for" loop. The error message is: "error: incompatible types: Object cannot be converted to String". But the strange thing his - no one is asking the compiler to convert an Object to String. I am referring to the "columns" field which is a specific type (ArrayList) so it should be able to iterate over that. That the type parameter of TestClass isn't specified shouldn't cause concern since the compiler doesn't need to know the type parameter, the field has a specific type. As can be seen from the first section, when specifying the type parameter it works. The third section shows that it the compiler could have made it work since a simple move of the value of the columns field into a local variable, and then looping over that, resolves it. But it really shouldn't make a difference. What gives?!

import java.util.ArrayList;
import java.util.List;

public class Tester {
    static class TestClass<T>
    {
        public List<String> columns = new ArrayList<String>();
    }
    
    public static void main(String[] args)
    {
        // When parameterized with Integer it works
        TestClass<Integer> et = new TestClass<Integer>();
        for(String s : et.columns) {
            System.out.println("Joy!");
        }
        
        // Doesn't work when not parameterized
        TestClass et2 = new TestClass();
        for(String s : et2.columns) {
            System.out.println("Joy!");
        }
        
        // It's OK if we just manually take out the columns field
        List<String> columns = et2.columns;
        for(String s : columns) {
            System.out.println("Joy!");
        }
    }
}

Someone pointed to What is a raw type and why shouldn't we use it? but I don't think it's exactly relevant. Obviously that would be relevant if the loop was over an array parameterized by because clearly the compiler could only then think of the ArrayList as being essentially ArrayList. But in this case the array doesn't depend on T, it's going to be a String no matter how the class is parameterized.

Morty
  • 1,706
  • 1
  • 12
  • 25
  • 1
    Does this answer your question? [What is a raw type and why shouldn't we use it?](https://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it) – OH GOD SPIDERS Jul 28 '20 at 12:34
  • Can you write a public constructor. – Saurabh Jhunjhunwala Jul 28 '20 at 12:35
  • 1
    When you use `TestClass` as a raw type, *all* types are erased. Meaning that it will be a `List columns` instead of `List columns` in the class. – Kayaman Jul 28 '20 at 12:36
  • @Reporter Yes, that compilation error is literally the subject of the question. – Michael Jul 28 '20 at 12:37
  • Re: "*It's OK if we just...*" the assignment of `et2.columns` (List) to variable of type `List` is unchecked, and compiler considers it unsafe. You could safely cast/suppress the warning though – Michael Jul 28 '20 at 12:38
  • Kayaman: Why would "String" also be erased? It is not related to the missing type parameter ? – Morty Jul 28 '20 at 12:54
  • @Michael: Yes but that just gives a warning, not a compilation error. IMHO it's surprising that case 2 and 3 are so close and semantically equivalent, and yet have such different behaviour when compiled. – Morty Jul 28 '20 at 12:54
  • 1
    @Morty "*Why would "String" also be erased?*" That's just how it works. Raw types were designed for backwards compatibility only. i.e. you either have full generics support or you don't. It probably makes for a simpler compiler impl and spec to erase everything rather than everything *for which a type is not provided*. You should never be using them in any new code anyway. (Effective Java item 26) – Michael Jul 28 '20 at 12:59
  • OK thanks then I learned something new. Didn't know that there was this concept of raw type that affected other aspects of the class. – Morty Jul 28 '20 at 17:11

0 Answers0