Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Typed Inner Classes in Java

I've been learning and experimenting with Java Generics for a while but I have run into something that I cannot explain. Take for example the following code:

public class Question {
    public <T> Sub<T> getSub(Class<T> c) {
        return new Sub<T>(c);
    }
    public class Sub<S> {
        private Class<S> c;
        public Sub(Class<S> c) {
            this.c = c;
        }
        public void add(S s) {
        }
    }
}

And the test code:

import generics.Question.Sub;

public class Answer {
    public static void main(String [] args) {
        Question q = new Question();
        Sub<String> s = q.getSub(String.class);
        s.add("");
    }
}

When this is run it gives a wonderfully cryptic error message:

C:\Answer.java:8: incompatible types
found   : generics.Question.Sub<java.lang.String>
required: generics.Question.Sub<java.lang.String>
        Sub<String> s = q.getSub(String.class);
1 error

Now, through some experimentation I have worked out how to prevent the compiler error. I can either make the Sub class a static inner class, or I need to refer to the Sub class as Question.Sub<String>. What I can't do is explain why I need to do this.

I've done some reading of the Java documentation on Generics but none cover this particular case.

Can anyone explain why the code is an incompatible type in its current form?

-Edit-

Looking at this closer I can see that I get the same behaviour outside of Netbeans. If I have the code in the following structure:

generics\
generics\Question.java
generics\Answer.java

When I compile the files together, I do not get the error:

C:\>javac generics\Question.java generics\Answer.java

C:\>

However, when I compile Question first and then Answer, I get the error:

C:\>javac generics\Question.java

C:\>javac generics\Answer.java
generics\Answer.java:8: incompatible types
found   : generics.Question.Sub<java.lang.String>
required: generics.Question.Sub<java.lang.String>
        Sub<String> s = q.getSub(String.class);
                                ^
1 error

I have heard something mentioned about Type Erasure. Is this the case in this situation?

like image 374
gencoreoperative Avatar asked Mar 02 '10 21:03

gencoreoperative


People also ask

What is generic type class in Java?

A Generic class simply means that the items or functions in that class can be generalized with the parameter(example T) to specify that we can add any type as a parameter in place of T like Integer, Character, String, Double or any other user-defined type.

What is a inner class in Java?

An inner class in Java is defined as a class that is declared inside another class. Inner classes are often used to create helper classes, such as views or adapters that are used by the outer class. Inner classes can also be used to create nested data structures, such as a linked list.

What is the generic class type called?

Generic Classes These classes are known as parameterized classes or parameterized types because they accept one or more parameters.


1 Answers

Type erasure is a property of the way generics are currently implemented in Java. What this means is that the type of the variables are only known at compile time, but not at runtime. So, for example, in the following:

Map<String,String> map = new HashMap<String,String>();

then the compiler knows to check against items being put in at a String/String basis. However, the compiled code doesn't know anything about the String,String - you can still insert objects in with the wrong type, e.g.:

Map other = (Map)map;
other.put(new Integer(3), new Double( 4.5 );

The problem is that the compiled code doesn't check for argument types when passing in, and nor does runtime (since the type information has been erased, hence, type erasure).

I doubt that type erasure is an issue here - since at compilation time, you have the full type information - but rather that it's probably a bug. There's quite a few hairy problems with generics (from an implementation) and there are different compilers in use with JavaC and Eclipse, so might exhibit different bugs. In some cases, the Eclipse compiler has been more faithful to the spec than the Sun compiler has (so Eclipse creates errors whilst Sun doesn't) and it's mostly due to the complexity of the way that the type system works.

So it's most likely one (or more) bugs with generics in the 1.5.0_14 compiler ...

like image 199
AlBlue Avatar answered Sep 21 '22 15:09

AlBlue