简体   繁体   中英

Are wildcard generics really needed?

for example:

    public String add(Set<?> t){
    ...;
    }



    public <T> String add(Set<T> t){
    ...;
    }

The first uses wildcard generics; the second is the normal form of a generic method. What's the difference?

In what situation do we need wildcard generics, not the normal form of generics?

Here is a situation where wildcards are required. This method takes a List<List<?>> , which is a list of lists. The method can add lists of different component types into it:

public void foo(List<List<?>> t) {
    t.add(new ArrayList<String>());
    t.add(new ArrayList<Integer>());
}

You cannot do this using generic type parameters without wildcards. For example, the following does not work:

public <T> void foo(List<List<T>> t) {
    t.add(new ArrayList<String>()); // does not compile
    t.add(new ArrayList<Integer>()); // does not compile
}

Since support for generics was added, using a parameterized type without providing a type parameter usually causes a compiler warning. On the other hand, there are situations where you don't care at all what the type parameter is (ie you don't use the type anywhere) or, even worse, you might not know what T is at all, and using <?> lets you express just that without causing a compiler warning.

Possible use case for the "don't care" case (very simple for brevity, but you get the idea):

public void clearList(List<?> list) {
    list.clear();
}

An example for the "don't know" case: an actual method signature from Class class:

static Class<?> forName(String className);

Here the method returns an object of some Class type. Class is generic but of course you don't know the type because it depends on the className parameter which is resolved at runtime. So you can't put T here since T is not known at compile time (even for a particular call site), and using just Class without type parameter would be a bad practice and cause a compiler warning.

The wildcard form is when you don't mind what types of objects you are handling.

The generics form allows you to add contraints on the type of objects handled.

An example use case could be the following : a generic repository with add/update/remove methods, you define common behavior using the generic type :

public class Repository<T>{
 public void add(T t){...}
 public void update(T t){...}
 public void remove(T t){...}
}

Then to make a repository for Apple and Banana you just extend this class and replace T with the real type :

public class AppleRepo extends Repository<Apple> {}
public class BananaRepo extends Repository<Banana> {}

If the generic Repository was declared as Repository<?> , it would not be good because it is not restricted to Banana, and you would not be able to use Banana specific methods inside it without casting objects;

Also the generics allow you to express further constraints, for example

Repository<T extends Fruit>

allows you to restrict the generic repository class to fruits. And you will be able to make calls to Fruit methods in its code.

There's not difference in calling the methods.

In the second method ( add(Set<T>) ) you can create variables of type T :

public <T> String add(Set<T> t){
    T item = t.iterator().next();
    //....
}

That gives you some additional type checking. In the first method you're left with using Object .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM