Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between using wildcards and declaring generic type in abstract method in Java

I'm trying to understand Generic types in Java, and in theory it looks understandable, but when I need to apply it to real code I have problem. I want to declare abstract method which will return generic type. Let's assume that I have some empty interface called Magicable and 2 class implements it: Magican and Witch. Now I wonder what is the difference between those 3 declarations:

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

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

    I have warning message:

    Type safety: The return type List<Magican> for getMagicable() from the type MagicanService needs unchecked conversion to conform to List<Magicable> from the type MagicableService.

  2. In second case I don't have this warning, but I have problem in abstract class in which I declared above abstract method:

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

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

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

  3. Third case causes compilation errors in both abovementioned places of code. I don't think if it is properly solution in my case.

like image 313
pumbosha Avatar asked Sep 12 '17 08:09

pumbosha


People also ask

Why do we need wildcard in generics in Java?

We can use the Java Wildcard as a local variable, parameter, field or as a return type. But, when the generic class is instantiated or when a generic method is called, we can't use wildcards. The wildcard is useful to remove the incompatibility between different instantiations of a generic type.

What is the use of wildcard in generics?

The question mark (?) is known as the wildcard in generic programming. It represents an unknown type. The wildcard can be used in a variety of situations such as the type of a parameter, field, or local variable; sometimes as a return type.

What is the difference between abstract class and generic class?

Abstract is used to define something that requires additional definition of functionality before it is considered "complete" (or concrete in Java-certification-test-terms). Generic means it's a class that can handle a wide variety of data types that you define when you instantiate the class.

Can wildcard types be replaced by any type?

Due to extensive capture conversion, in most places, compiler treats wildcards as if they are type variables. Therefore indeed programmer can replace wildcard with type variables in such places, a sort of manual capture conversion.

What are wildcards arguments in generics in Java?

What are wildcards arguments in Generics In Java? Generics is a concept in Java where you can enable a class, interface and, method, accept all (reference) types as parameters. In other words it is the concept which enables the users to choose the reference type that a method, constructor of a class accepts, dynamically.

What is a generic method in wild cards?

Wild Cards (?) Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type at class level, but the type parameter’s scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.

What are the types of wild cards in Java?

Local field. The only restriction on wilds cards is that you cannot it as a type argument of a generic method while invoking it. Java provides 3 types of wild cards namely upper-bounded, lower-bounded, un-bounded. Upper bounds in wild cards is similar to the bounded type in generics.

When to use wildcards in JavaScript?

We generally use this wildcard when the code inside the method is using the Object functionality and also when the code inside the method does not depend upon the parameter type. So if we briefly wrap up about each wildcard, we can say their usage in the following three ways: Upper bound Wildcard: ? extends Type. Lower bound Wildcard: ? super Type.


2 Answers

  1. 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 have to declare your generic T into the class defintion

     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

The compilation error is normal because <? extends Magicable> from the signature of method means you don't care what's inside your list from the moment you can consider those elements just as Magicable. When doing a call

    List<T> list = getMagicables();

You want to take care of the type T without knowing it. In other terms, there are 3 use cases: T is Magicable (OK), T is Magician (Wrong because getMagicables may return a list of Witch) and T is Witch (Wrong too).

  1. Why I use ? extends Magicable instead of just Magicable in lists

Because List<Magician> is a subtype of List<? extends Magicable> but not a subtype of List<Magicable>. This is usefull for parameters of methods.

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

may 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 can't use it as

    List<Witch> list = ...
    doIt(list); // compile error
like image 171
Max Avatar answered Sep 24 '22 23:09

Max


For the part of the problem, you did show us, method /* 3 */ is sufficient, you do not need the generics for that part of your code. But you need to respect substitutability:

You get the error in #1 because the sub-type method is restricting the range of the return type: a Magican is Magicable but not vice versa. Super-types of Magicable are allowed in the sub-type. The sub-type method has to be substitutable for the super-type method, which is not the case in your example.

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

like image 36
Stefan Avatar answered Sep 20 '22 23:09

Stefan