Java通配符有界泛型

时间:2018-08-06 10:58:28

标签: java generics

我正在努力使用Java 8通配符泛型。

假设一个名为Pair<T>的通用类(来自Core Java书)

class Pair<T> {
    private T first;
    private T second;

    public Pair() {
        first = null;
        second = null;
    }
    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }
    public T getFirst() { return first; }
    public T getSecond() { return second; }
    public void setFirst(T newValue) { first = newValue; }
    public void setSecond(T newValue) { second = newValue; }
}

假定以下类层次结构:
基本雇员(层次结构顶部),然后
     经理扩展员工,然后
     行政人员扩展经理

下面的代码可以工作,但我不明白为什么允许这样做。

Pair<? super Manager> pm2 = 
    new Pair<>(
      new Employee(1,"Yuri"), // Employee is super of Manager
      new Executive()); // Executive is not super of Manager 
// why Executive is allowed in above Pair<T> ?

Employee ex1 = (Employee) pm2.getFirst(); // OK
Manager ex2 = (Manager) pm2.getSecond(); // OK
Executive ex3 = (Executive) pm2.getSecond(); // why is allowed?

我不理解执行程序为什么不是上面提到的工作,因为它不是管理器的超级类型,而是管理器的子类型。

是因为Java 8编译器转换了吗?超级管理员对象,因此可以允许任何操作吗?

2 个答案:

答案 0 :(得分:1)

您声明的是:

Pair<? super Manager> pm2 = ...

接受泛型的Pair方法可以用Manager和该类的子类(在您的情况下为Executive)代替泛型。

从逻辑上讲,当您使用泛型调用方法时,将得到以下结果:

Pair<? super Manager> pm2 = ...;        
pm2.setFirst(new Executive());  // compile
pm2.setFirst(new Manager());  // compile
pm2.setFirst(new Employee()); // doesn't compile

最后,就像您没有使用任何通配符一样:

Pair<Manager> pm2 = ...;        
pm2.setFirst(new Executive());  // compile
pm2.setFirst(new Manager());  // compile
pm2.setFirst(new Employee()); // doesn't compile

因此在您的示例中,您使用的下界通配符是无助的。
实际上,这是最糟糕的情况,因为您同时使用它来从泛型类型中放入和获取事物,而下界通配符旨在放入而不是从中获取事物。
而您必须执行的转换却违反了通用目的(类型安全):

Employee ex1 = (Employee) pm2.getFirst(); // OK
Manager ex2 = (Manager) pm2.getSecond(); // OK
Executive ex3 = (Executive) pm2.getSecond(); // why is allowed?

那么我们该如何使用Pair<? super Manager>
我们希望可以将通用集合Pair分配给其他Pair类型,以将事物“放入”。
最常见的用例是声明一个接受通用类型<? super>的方法。
但是在这种情况下,我们希望将未知类型限制为特定类型或父类型,以防止“放入”可能损害作为参数传递的泛型类型的类型安全的事物。

例如,假设您需要一个方法,该方法接受Pair个实例中的Manager个并将其放入此Pair中。 您不希望客户方法可以传递Pair<Executive>,否则如果我们添加Manager可能会破坏通用安全性,因为Manager不一定是Executive实例。这是下界通配符的主要作用。

示例代码说明:

public void putStuff(Pair<? super Manager> managers) {
    if (...){
       managers.setFirst(new Manager());
    }
    else if (...){
    managers.setFirst(new Executive());
   }
}

对于Manager泛型类型,现在只能传递Pair的类型或超类型:

putStuff(new Pair<Manager>(...)); // compile
putStuff(new Pair<Employee>(...)); // compile
putStuff(new Pair<Executive>(...)); // doesn't compile

答案 1 :(得分:0)

在Java中,最高级的类型是java.lang.Object。换句话说,每个单个类的父类是java.lang.Object

通过定义的Pair<? super Manager>,您标记了Pair类可能持有的通用类型的下限。请参阅文档HERE

在您的示例中,似乎Executive也是Employee的子类型,因此很显然它共享相同的超级类型,并且也共享java.lang.Object