通用vs通配符未知类型

时间:2015-03-21 03:22:14

标签: java generics wildcard

建议何时执行:

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

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

看起来它们的效果也一样。

5 个答案:

答案 0 :(得分:4)

没有类型返回值,我能想到的唯一区别是在方法调用期间显式输入第一种声明方式。

例如,您在类型类C<K extends String>

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

会引发编译错误。但是为什么有人想要这样做......

很好的问题,即使答案很可能两者都是一样的。

答案 1 :(得分:2)

明确地将通用类型命名为E而不是?具有这些用途(据我所知):

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)将参数类型的某些部分绑定到封闭类型的某些部分:

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

2)绑定参数类型的一些组合,返回类型和封闭类型(参见#0和#1)。

如果我们不关心实际类型,我们可以使用匿名类型名称?。这是一个基本的例子:

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".

另一个例子:

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".

替代阅读:http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html

答案 2 :(得分:2)

我想在那个特定的例子中,它们在类型检查方面都以相同的方式有效地工作。但是,如果将泛型类型扩展为需要某个类的基类或超类,则它可能很有用。 e.g。

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

这至少强制您接收User的一些子类。

修改

您可以使用通配符来实现相同的目标:

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

但是,如果您想要将泛型用于多个参数,那么这将无效:

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

这会对两个参数强制执行泛型类型,而以下代码不会强制两个列表包含相同的类型:

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

我可以使用Automobile类的两个不同的子类调用该方法:

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

使用泛型对两个参数强制执行相同的类型。

答案 3 :(得分:2)

泛型和通配符未知类型之间的差异:

  • 对方法参数的类型实施关系(使用泛型)
  • 支持多边界(使用泛型)
  • 支持上限和下限(使用通配符)

相关问题:

When to use generic methods and when to use wild-card?

答案 4 :(得分:1)

当您只需要从列表中检索时使用? extends

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

只需添加到列表时使用? super

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

两者需要检索并添加:

时,请使用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:我们不知道列表的确切类型,但我们可以从中检索User
  • ? super User:我们不知道列表的确切类型,但我们可以在其中添加User
  • E extends User:我们不一定知道列表的确切类型,但它符合以下限制:
    • 我们为其实际类型指定名称E
    • 我们知道E至少是User
    • 我们都可以从列表中检索E并将E放入列表中。

另见: