java泛型中的有界通配符

时间:2014-02-12 22:39:34

标签: java generics wildcard bounded-wildcard

我正在Core Java volume-1 by Horstmann, Cay.S上阅读generics。我无法理解教科书的一些解释。我给出了作者所指的示例代码。然后是书中的文字和我的问题。

 class Employees{ }

class Manager extends Employees { }

class Pair<T>{

    private T first;
    private T second;

    Pair(T first, T second){
        this.first=first;
        this.second=second;
    }


    void setFirst(T value){
        first=value;
    }

    T getFirst(){
        return first;
    }

}


public class WildCardGenerics {

    public static void main(String[] args){


        Manager ceo = new Manager();
        Manager cfo=new Manager();

        Employees clerk = new Employees();

        Pair<Manager> managers= new Pair<>(ceo,cfo);
        Pair<? extends Employees> employees=managers;

        employees.setFirst(clerk);



    }
    }

从书中可以看出:

“”“不存在腐败。对setFirst的致电是type error。为了了解原因,让我们更接近 查看类型Pair<? extends Employee>。它的方法如下:

? extends Employee getFirst()
void setFirst(? extends Employee)

这使得无法调用setFirst方法。 compiler只知道它需要一些subtype Employees,但它不知道哪种类型。毕竟,它拒绝pass any specific type ?可能与之不符。“”“

问题: 我不明白为什么它拒绝传递任何特定类型。那么这种方法接受了什么? ? extends Employee - 表示包括Employee和Employee的任何子类型。通过员工应该是合法的吗?

从书中可以看出:

pair<? super Manager> has methods:
void setFirst(? super Manager)
? super Manager getFirst()

编译器不知道setFirst方法的确切类型,因此无法调用它 使用Employee or Object类型的对象,但只能使用Managersubtype,例如executive

问题: 所以在这里我说该方法可以接受任何Manager对象或扩展Manager对象(类似于上面的subytpe)。我不清楚为什么?

从书中可以看出:

Intuitively speaking, wildcards with supertype bounds let you write to a generic object, while wildcards with subtype bounds let you read from a generic object.

问题: 我根本无法遵循它。这听起来很简单,但并不遵循逻辑意义。

2 个答案:

答案 0 :(得分:1)

以下分配是合法的,前提是ClerkEmployees [sic]。

Pair<? extends Employees> employees = new Pair<Clerk>(clerk1, clerk2);

您应该无法致电setFirst并传递Manager,因为在这里,它确实是Pair<Clerk>

这说明了如果存在T通配符,Java不允许调用其中包含泛型类型参数的方法(? extends)。编译器不知道它到底是哪个类 - Employee?一个子类?哪个子类?由于类型擦除,JVM无法检查类型安全性,因此编译器必须禁止除null之外的所有此类调用,这些调用可以转换为任何引用类型并且始终允许。

使用? super Employee时,下限为Employee。它可能是Pair<Object>。但是,传递Employee或其子类之一是安全的,因为它们是? super Employee允许的任何子类。

答案 1 :(得分:1)

始终将? extends T视为“某些CAP#1延长T”,将? super T视为“CAP#2延伸的T。” / p>

现在你的例子更具可读性:

“没有腐败是可能的。对setFirst的调用是一个类型错误。要了解原因,让我们仔细看看类型Pair< some CAP#1 extending Employee >。它的方法如下:

CAP#1 getFirst()
void setFirst( CAP#1 value )

由于CAP#1是一种虚构类型,因此您无法使用setFirst()来呼叫Employee,因为您无法知道CAP#1是否为{{1}或者它的一些随机子类型。 Employee逆变方法;它使用泛型类型的实例。

但是,无论setFirst()是什么,它都会延伸CAP#1,因此您可以致电Employee并将结果分配给getFirst() - 变量。 Employee协变;它生成泛型类型的实例。

所以你问,如果你不能在这样的变量上使用一半的方法,为什么你会想要一个getFirst()类型的变量呢?

因为你可以使用通配符类型做的一件事是解决Java中的一些方差问题。无论我有Pair< ? extends Employee >还是Pair< Employee >还是Pair< Manager >,我都可以将该内容分配给Pair< SomeClassExtendingManager >类型的变量。我无法将Pair< ? extends Employee >分配给Pair< Manager >类型的变量,即使Pair< Employee >中没有逆变方法,因为类型系统无法解决这个问题。