Search code examples
javagenericsgeneric-programming

Understanding of generic forwarding during inheritance in Java


I have such code:

interface A<T extends A> {
    T method();
}

class AIml<T extends A> implements A<T> {
    @Override
    public T method() {
        return (T) this;
    }
}

My questin is: Why we can't write return this; in method implementation.

As I underood after generic erasing this code should become like this.

interface A {
    A method();
}

class AIml implements A {
    @Override
    public A method() {
        return (A)this;
    }
}

And class casting becomes redundant.

So is it compiler not smart enough here? Or I miss something and ClassCastException can happens here?


Solution

  • When you declare generic type <T extends A> it means that you want to accept some type which explicitly or implicitly extends/implements A type. Nothing more, nothing less.

    You can have many types which fulfill these requirements. Lets say you have

    class Foo implements A<Foo>{...}
    

    or even

    class Bar implement A<Foo>{...} //<-- generic type doesn't need to be Bar,
                                    //    *some* other type which extends A is OK
    

    Now when you are declaring T as

    class AIml<T extends A> implements A<T> {...}
    

    you are agreeing to same conditions as before, so it is legal to have AIml<Foo>. Problem is that Foo isn't really related to AIml so you can't return this as its representation.

    Or I miss something and ClassCastException can happens here?

    While you are right that

    public T method() {
        return (T) this;
    }
    

    will be erased to

    public T method() {
        return (A) this;
    }
    

    it will allow you to only use it safely with code like

    AIml<Foo> aimp = new AIml<Foo>();
    A methodResult = aimp.method();
    

    but you will get ClassCastException if you will want to hold result of method() in reference variable of same type as T

      AIml<Foo> aimp = new AIml();
      Foo methodResult = aimp.method();
    //^^^-- since method() returned AIml, not Foo