JDK-8343580 : Type error with inner classes of generic classes in functions generic by outer
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 21
  • Priority: P4
  • Status: In Progress
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2024-11-04
  • Updated: 2025-05-15
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 25
25Unresolved
Related Reports
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
The test method fails to compile, as javac believes that getter.get() returns just Object:

class Scratch {
    static abstract class Getters<T> {
        abstract class Getter {
            abstract T get();
        }
    }

    static class Usage<T, G extends Getters<T>> {
        public T test(G.Getter getter) {
            return getter.get();
        }
    }
}


FREQUENCY : always



Comments
I agree with you in principle... in practice, it seems that what's happening (and I admit I'm guessing here) is that "G.Getter" is being interpreted as a raw type, just like it would if you had said "Getters.Getter". I haven't checked whether this is correct according to the JLS or not.
09-12-2024

I checked a while ago the compiled class files - the generic signature generated for G.Getter is Getters.Getter, where Getters is erased. I don't think this is part of type erasure, because erasure should not happen during the generic signature generation. It might be some other step that is wrong.
09-12-2024

I'm guessing the problem is that the test method takes a parameter of type "G.Getter". The "G" there is functioning like a raw type (due to type erasure), even though at compile time G represents a sub-type of Getters<T>. Here's a way to do what I think you are trying to do that compiles: class Scratch { static abstract class Getters<T> { abstract class Getter { abstract T get(); } } static class Usage<T> { public T test(Getters<T>.Getter getter) { return getter.get(); } } }
09-12-2024

Additional information from the submitter: I believe this is indeed a bug. Could you clarify using the Java standard, why do you think that the cast is supposed to be there? Because for example the test2 method successfully resolves the correct type and I don't see why this should be different in the initial example. class Scratch { static abstract class Getters<T> { abstract class Getter { abstract T get(); } } static class Usage<T, G extends Getters<T>> { public T test(G.Getter getter) { return getter.get(); } static <T> T test2(Getters<T>.Getter getter) { return getter.get(); } } public static void main(String[] args) { } }
06-11-2024

The line return getter.get(); needs to cast the return type from object to T return (T)getter.get(); Closed as not an issue.
05-11-2024