简体   繁体   English

真的需要通配符泛型吗?

[英]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. 此方法采用List<List<?>> ,这是一个列表列表。 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. 另一方面,有些情况下你根本不在乎类型参数是什么(即你不在任何地方使用这种类型),或者更糟糕的是,你可能根本不知道T是什么,并且使用<?>让你表达这一点,而不会导致编译器警告。

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: “不知道”案例的一个例子:来自Class类的实际方法签名:

static Class<?> forName(String className);

Here the method returns an object of some Class type. 这里该方法返回一些Class类型的对象。 Class is generic but of course you don't know the type because it depends on the className parameter which is resolved at runtime. Class是通用的,但当然您不知道类型,因为它依赖于在运行时解析的className参数。 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. 所以你不能把T放在这里因为T在编译时是不知道的(即使对于一个特定的调用站点),并且只使用没有类型参数的Class会是一个不好的做法并导致编译器警告。

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 : 示例用例可以是:具有add / update / remove方法的通用存储库,您可以使用泛型类型定义常见行为:

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 : 然后为AppleBanana创建一个存储库,你只需要扩展这个类并用实际类型替换T:

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; 如果通用存储库被声明为Repository<?> ,那就不好了,因为它不仅限于Banana,如果没有强制转换对象,你将无法在其中使用Banana特定的方法;

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. 并且您将能够在其代码中调用Fruit方法。

There's not difference in calling the methods. 调用方法没有区别。

In the second method ( add(Set<T>) ) you can create variables of type T : 在第二种方法( add(Set<T>) )中,您可以创建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 . 在第一种方法中,您将使用Object

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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