The difference between using wildcards and a generic type declaration in an abstract method in Java

I am trying to understand common types in Java, and theoretically this seems understandable, but when I need to apply it to real code, I have a problem. I want to declare an abstract method that will return a generic type. Suppose I have an empty interface called Magicable and 2 classes implement it: Magican and Witch. Now I wonder what is the difference between these 3 ads:

/*1*/protected abstract <T extends Magicable> List<T> getMagicables(); /*2*/protected abstract List<? extends Magicable> getMagicables(); /*3*/protected abstract List<Magicable> getMagicables(); 
  • In the first case, I have a problem when I want to implement the body of this method in some class that extends the abstract class:

     @Override protected List<Magican> getMagicable() {..} 

    I have a warning message:

    Security Type: The return type of the <Magican> list for getMagicable () from the MagicanService type requires an raw conversion to match the List <Magicable> of the MagicableService type.

  • In the second case, I do not have this warning, but I have a problem in an abstract class in which I wrote the abstract method above:

      public void <T extends Magicable> T getOneFromList() { List<T> list = getMagicables(); //..... } 

    In this case, I have a compilation error in the getMagicables () call:

    Type mismatch: cannot convert from <capture # 2-of? extends Magicable> to list <T>

  • The third case causes compilation errors in both of the above code locations. I do not think that if this is the right decision in my case.

+7
java generics inheritance abstract
source share
4 answers
  • First case

Just declare your method with:

  @Override protected <T extends Magicable> List<T> getMagicables() { List<T> list = ... return list } 

If you really want this:

  @Override protected List<Magican> getMagicable() {..} 

you may need to declare your generic T in defintion class

  public abstract class AbstractKlass<T extends Magicable> { protected abstract List<T> getMagicables(); } 

then in your subclass:

  public class MySubClass extends AbstractKlass<Magican> { @Override protected List<Magican> getMagicables() { ... } } 
  1. Second case

A compilation error is normal because <? extends Magicable> <? extends Magicable> from a method signature means that it doesn't matter to you what’s inside your list from the moment you can view these elements in the same way as Magicable. Making a call

  List<T> list = getMagicables(); 

You want to take care of type T without knowing it. In other words, there are 3 uses: T is magic (OK), T is a wizard (wrong because getMagicables can return a Witch list), and T is a witch (also incorrect).

  1. Why am I using ? extends Magicable ? extends Magicable instead of just Magicable on lists

Because List<Magician> is a subtype of List<? extends Magicable> List<? extends Magicable> , but not a subtype of List<Magicable> . This is useful for parameter methods.

  public void doIt(List<? extends Magicable> list) { // you can't add a Magician here } 

can be used as

  List<Witch> list = ... doIt(list); 

But if you have

  public void doIt(List<Magicable> list) { // you can add a Magician here } 

You cannot use it as

  List<Witch> list = ... doIt(list); // compile error 
+2
source share

For the part of the problem that you showed us, the / * 3 * / method is sufficient; you do not need generics for this part of your code. But you need to respect substitution:

You get an error in # 1 because the subtype method limits the range of the return type: a Magican is Magicable , but not vice versa. Magicable are allowed in the subtype. The subtype method should be replaced for the supertype method, which does not match your example.

? error in # 2 due to the nature of the wildcard ? : ? extends Magicable ? extends Magicable and T extends Magicable need not be of the same type. If T declared in the scope of the class, for example. class Magican<T> implements Magicable<T> (of course, your interface must declare T in this case), all occurrences of T in your type will refer to the same class.

+1
source share
 public abstract class AbstractMagicable<T extends Magicable> { abstract List<T> getMagicables1(); abstract List<? extends Magicable> getMagicables2(); abstract List<Magicable> getMagicables3(); } class MagicableWitch extends AbstractMagicable<Witch> { @Override List<Witch> getMagicables1() { return null; } @Override List<? extends Magicable> getMagicables2() { return getMagicables1(); } @Override List<Magicable> getMagicables3() { return Collections.singletonList(new Witch()); } } class MagicableMagician extends AbstractMagicable<Magician> { @Override List<Magician> getMagicables1() { return null; } @Override List<? extends Magicable> getMagicables2() { return getMagicables1(); } @Override List<Magicable> getMagicables3() { return Collections.singletonList(new Magician()); } } 

1) T is used when you want to replace it with a real name during its use. For example class MagicableWitch extends AbstractMagicable<Witch> .

Here, Witch replaced T and therefore abstract List<T> getMagicables1(); replaced by List<Witch> getMagicables1() in its specific class.

2)? used when the class to be replaced is available at run time.

3) List<Magicable> and List<Witch> different, although Witch implements Magicable . The implementation is shown in getMagicables3 .

+1
source share

In your first case, the abstract method is declared as using the generic type <T extends Magicable> , which means that your method can return a Magicable list or any type that implements it. In your implementation, you return a specific type of Magican that is Magicable. You can safely ignore the warning and add @SuppressWarning("unchecked") to disable the warning. Keep in mind that any classes that extend your class will be limited to returning only Magican lists.

In the second case, the declaration List<T> list = getMagicables(); throws an error message because your method does not return List<T> , but List<? extends Magicable' List<? extends Magicable' , which is not the same. Due to the way generics work, when you declare a return type that uses an unrelated wildcard, any code that calls your method must have an accepted match type in your case, such as List<? extends Magicable> List<? extends Magicable> or List<?> .

As for the third case, your abstract method returns a List<Magicable> , and your implementation returns a List<Magic> . This may seem counterintuitive, but you cannot do something similar with generics in Java: List<Magicable> list = ArrayList<Magic> . This may seem strange, since arrays allow you to declare something like Magicable[] magics = new Magican[3]; . This is a common misconception, since arrays are covariant, while generics are invariant. Covariant means that if you have two classes Super and Sub extends Super , Sub[] is a subtype of Super[] . For generics, since they are invariant, there is no relationship between the two, and List<Sub> not a subtype of List<Super> .

If you want to return a generic type, just use the same type declaration as the first case of protected <T extends Magicable> List<T> getMagicable() in classes that extend your abstract class. It is a very bad idea to use wildcards in the return type, since you force your class users to use wildcards in the declarations of List variables.

+1
source share

All Articles