10

Let's say I have a super-class that defines the following abstract method

public abstract <T extends Interface> Class<T> getMainClass();

Now if I want to override it in some sub-class

public Class<Implementation> getMainClass(){
    return Implementation.class;
}

I get a warning about type safety and unchecked conversion:

Type safety: The return type Class<Implementation> for getMainClass() from the type SubFoo needs unchecked conversion to conform to Class<Interface> from the type SuperFoo

Doesn't Class<Implementation> fall under Class<T> if <T extends Interface>? Is there any way to properly get rid of the warning?

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
alh84001
  • 1,263
  • 1
  • 15
  • 25

4 Answers4

14

the overriding method's return type must be a subtype of the overridden method's return type.

Class<Impl> is not a subtype of Class<T> where <T extends Interface>. T is unknown here.

Class<Impl> is a subtype of Class<? extends Interface>, per subtyping rules.


some subtyping rules regarding wildcards:

for any type X

  • A<X> is a subtype of A<? extends X>

  • A<X> is a subtype of A<? super X>

if S is subtype of T

  • A<? extends S> is a subtype of A<? extends T>

  • A<? super T> is a subtype of A<? super S>

More concisely, ( <: means "is a subtype of" )

A<S>    <:    A<? extends S>    <:    A<? extends T>

A<T>    <:    A<?  super  T>    <:    A<?  super  S>
irreputable
  • 44,725
  • 9
  • 65
  • 93
  • This solved the problem completely. Thank you. On a sidenote, what would be a good reference for these kinds of questions on generics, explaining e.g. subtyping rules? – alh84001 Mar 02 '11 at 09:41
  • 2
    There's [Angelika Langer's generics FAQ](http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html) - it covers just about every edge case, and yet each section is (relatively) easy to take in and understand. – Andrzej Doyle Mar 02 '11 at 10:38
  • @alh84001 unfortunately, I don't know a better source than Java Language Specification. – irreputable Mar 02 '11 at 19:39
3

Consider the following scenario similar to yours:

public class SuperFoo {
     public abstract <T extends Interface> List<T> getList();
} 

public class SubFoo extends SuperFoo {
     private List<Implementation> l = new ArrayList<Implementation>();

     public List<Implementation> getList() {
          return l;
     }

     public void iterate() {
          for (Implementation i: l) ...;
     }
}

SubFoo subFoo = new SubFoo();
SuperFoo superFoo = subFoo;
superFoo.getList().add(new AnotherImplementation()); // Valid operation!
subFoo.iterate(); // Unexpected ClassCastException!

In this case unchecked conversion warning warns you about possibility of unexpected ClassCastException.

However, in your case, when return type is Class<...>, it's not a problem (as far as I understand), so you can legally suppress a warning:

@SuppressWarnings("unchecked")
public Class<Implementation> getMainClass(){ ... }  

Another option is to make SuperFoo itself generic:

public class SuperFoo<T extends Interface> {
    public abstract Class<T> getMainClass(); 
}

public class SubFoo extends SuperFoo<Implementation> {
    public Class<Implementation> getMainClass() { ... }
}

For yet another (and perhaps the best) option see Stas Kurilin's answer.

axtavt
  • 239,438
  • 41
  • 511
  • 482
1

try this

public abstract Class<? extends Interface> getMainClass();

reorganized example by such warnings java tried prevents cases like this

class OtherImpl implements Interface{ 
} 
A a = new B();//where A - your abstract class and B - implementation
Class<OtherImpl> other = a.<OtherImpl>getMainClass();//some broken think, But _without_ runtime exception

As @axtavt mentioned example was broken. I reorganized it.

Stan Kurilin
  • 15,614
  • 21
  • 81
  • 132
  • It wouldn't cause exception due to type erasure. – axtavt Feb 28 '11 at 17:12
  • Thank you for the example. It makes things clearer. Unfortunately I get the same warning when using your suggestion, but that is what I expected. I guess I'll leave it like it was. – alh84001 Feb 28 '11 at 17:15
0

Why do you want to have something like public abstract Class<? extends Interface> getMainClass(); rather than public abstract Interface getMainClass();?

I think you can simply return an instance of Interface, and then, if the caller wants to have access to the underlying runtime class can simply call getClass() on the returned object.

Essentially, I think you can simply do

public InterfaceImpl implements Interface {
    // ...
};

public abstract class A {
    public abstract Interface getMainClass();
    // ...
}

public class AImpl {
    return new InterfaceImpl();
}

public class Main {
    public static void main(String[] args) {
        AImpl aImpl = new AImpl();
        Interface i = aImpl.getMainClass();
        System.out.println(i.getClass());
    }
}
MarcoS
  • 13,386
  • 7
  • 42
  • 63