简体   繁体   English

通用vs通配符未知类型

[英]Generic vs wildcard unknown types

When is it recommended to do: 什么时候建议做:

public <E> boolean hasPropertyX(List<E extends User> alist);

versus

public boolean hasPropertyX(List<? extends User> alist);

It would appear they both work just as well. 看起来它们的效果也一样好。

Without typed return value, the only difference I can think of is explicit typing of the first way of declaration during method call. 没有类型返回值,我能想到的唯一区别是在方法调用期间显式输入第一种声明方式。

So for example you are using it inside typed class C<K extends String> 例如,您在类型C<K extends String>中使用它

List<V extends String> input = ...;
boolean var = obj.hasProperty<K>(input);

will raise the compiler error. 会引发编译错误。 But why would any one want to do so... 但是为什么有人想要这样做......

Nice question even if most likely the answer is both are the same. 很好的问题,即使答案很可能两者都是一样的。

Explicitly naming the generic type as E and not ? 明确地将泛型类型命名为E而不是? has these uses (as far as I can think of): 有这些用途(据我所知):

0) To tie the return type to some part of the argument type - for example: 0)将返回类型绑定到参数类型的某些部分 - 例如:

public <E> E getSomeElement(List<E> lst) { ... }
// ^ If we don't name the argument type as having E,
// then we can't specify the return type as being E

1) To tie some part of the argument type to some part of the enclosing type: 1)将参数类型的某些部分绑定到封闭类型的某些部分:

class Storage<E> {
    E item;
    public void replace(Storage<E> st) { item = st.item; }
    // ^ This wouldn't work if we wrote Storage<?> instead
}

2) To tie some combination of the argument types, return type, and enclosing type (see #0 and #1). 2)绑定参数类型,返回类型和封闭类型的一些组合 (参见#0和#1)。

We can get away with the anonymous type name ? 我们可以逃脱匿名类型名称? if we don't care about the actual type. 如果我们不关心实际类型。 Here is a basic example: 这是一个基本的例子:

boolean allEqual(List<?> lst, Object y) {
    for (Object x : lst) {  // Any reference can be stored as Object
        if (!y.equals(x))  // equals takes an Object
            return false;
    }
    return true;
}
// ^ We could also rewrite this example with List<E> and "E x".

Another example: 另一个例子:

int intSum(List<? extends Number> lst) {
    int sum = 0;
    for (Number x : lst)  // We only care that the list element is a Number
        sum += x.intValue();
    return sum;
}
// ^ We could also rewrite with List<E extends Number> and "E x".

Alternate reading: http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html 备选阅读: http//docs.oracle.com/javase/tutorial/extra/generics/wildcards.html

I suppose in that particular example they both do work effectively the same way in terms of type checking. 我想在那个特定的例子中,它们在类型检查方面都以相同的方式有效地工作。 However, if you extend the generic type to require a base or superclass of some class it can be useful. 但是,如果将泛型类型扩展为需要某个类的基类或超类,则它可能很有用。 eg 例如

public <E extends User> boolean hasPropertyX(List<E> alist);

This at least enforces that you're receiving some subclass of User . 这至少强制您接收一些User子类。

EDIT 编辑

You can use a wildcard to achieve the same thing: 您可以使用通配符来实现相同的功能:

public boolean hasPropertyX(List<? extends User> alist);

But this won't work if, for example, you want to use the generic for multiple parameters: 但是,例如,如果要将泛型用于多个参数,则无法执行此操作:

public <E extends Automobile> void crashAutos(List<E> list1, List<E> list2);

This enforces the generic type on both arguments, whereas the following code does not force the two lists to contain the same type: 这会对两个参数强制执行泛型类型,而以下代码不会强制两个列表包含相同的类型:

public void crashAutos(List<? extends Automobile> list1, List<? extends Automobile> list2);

I could call that method with two different subclasses of the Automobile class: 我可以使用Automobile类的两个不同子类调用该方法:

List<Car> cars = ...
List<Truck> trucks = ...
crashAutos(cars, trucks);

Whereas using generics enforces the same type for both arguments. 而使用泛型对两个参数强制执行相同的类型。

Differences between generics and wildcard unknown types: 泛型和通配符未知类型之间的差异:

  • Enforcing a relationship on the types of method arguments (Use Generics) 在方法参数的类型上实现关系(使用泛型)
  • Supporting multiple bounds (Use Generics) 支持多边界(使用泛型)
  • Supporting both upper and lower bounds (Use Wildcard) 支持上限和下限(使用通配符)

The related question: 相关问题:

When to use generic methods and when to use wild-card? 何时使用通用方法以及何时使用通配符?

Use ? extends ? extends ? extends when you only need to retrieve from the List: 当您只需要从List中检索? extends

User getElement(List<? extends User> list, int i) {
    return list.get(i);
}

Use ? super ? super ? super when you only need to add to the List: 当你只需要添加到List时? super

void addElement(List<? super User> list, User u) {
    list.add(u);
}

Use E extends when you both need to retrieve and add: 使用E extends ,当你需要检索并添加:

<E extends User> void swapElements(List<E> list, int i, int j) {
    E temp = list.get(i);
    list.set(i, list.get(j));
    list.set(j, temp);
}
  • ? extends User ? extends User : We don't know the exact type of the List, but we can retrieve a User from it. ? extends User :我们不知道List的确切类型,但我们可以从中检索User
  • ? super User ? super User : We don't know the exact type of the List, but we can put a User in it. ? super User :我们不知道列表的确切类型,但我们可以将User放入其中。
  • E extends User : We don't necessarily know the exact type of the List but it conforms to constraints such that: E extends User :我们不一定知道List的确切类型,但它符合约束,例如:
    • We give its actual type the name E . 我们给它的实际类型命名为E
    • We know E is at least User . 我们知道E至少是User
    • We can both retrieve E from the List and put E in the List. 我们都可以从List中检索E并将E放入List中。

See also: 也可以看看:

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

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