【问题标题】:bounded wildcards in java generics [duplicate]java泛型中的有界通配符[重复]
【发布时间】:2014-02-12 22:39:33
【问题描述】:

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

 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&lt;? extends Employee&gt;。它的方法如下所示:

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

这使得无法调用setFirst 方法。 compiler 只知道它需要Employees 中的一些subtype,但它不知道是哪种类型。它拒绝pass any specific type - 毕竟 ?可能不匹配。"""

问题: 我不明白为什么它拒绝传递任何特定类型。那么这个方法接受什么? ? extends Employee -- 表示包括 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.

问题: 我根本无法遵循它。听上去很简单,但不符合本意。

【问题讨论】:

    标签: java generics wildcard bounded-wildcard


    【解决方案1】:

    如果ClerkEmployees [原文如此],则以下分配是合法的。

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

    您不应该调用setFirst 并传递Manager,因为这里真的是Pair&lt;Clerk&gt;

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

    对于? super Employee,下限为Employee。可能是Pair&lt;Object&gt;。但是,传递Employee 或其子类之一是安全的,因为它们是? super Employee 允许的任何东西的子类。

    【讨论】:

    • This illustrates why Java doesn't allow calling a method with the generic type parameter in it (T) if there is a ? extends wildcard-- 如果不能调用,那么拥有它的目的是什么?
    • 您仍然可以访问该对中的元素,并保证它们是 Employee 的实例。阅读您获得的有关 PECS 的链接。 ? extends T 允许从容器中获取信息。 ? super T 允许在容器内设置信息。有关实际示例,请参阅List.addAll()。 addAll 可以从作为参数传递的集合中读取所有元素,但不会修改此列表。因此,您可以在 List 上使用 addAll() 并将 List 传递给它。
    • @user1988876 您可以使用相同的变量引用Pair&lt;Employee&gt;Pair&lt;Manager&gt;Pair&lt;Clerk&gt;。这样做的代价是你不能调用这样的方法并且仍然保持类型安全。
    • 所以通配符作为方法参数最有用,而不是像List&lt;? extends Number&gt; foo3 = new ArrayList&lt;Integer&gt;();这样的简单赋值,对吗?或者,如果我知道 foo3 将成为 ArrayList&lt;Integer&gt;(),那么这项作业有什么好处?
    • 将具有已知类型参数的类的对象分配给具有通配符的变量没有任何好处。正如你现在所看到的,它是有限制的。将其作为参数提供给方法更有优势,这样方法就更灵活了,可以接受更广泛的类型。
    【解决方案2】:

    始终将? extends T 读作“一些CAP#1 扩展T”,将? super T 读作“一些CAP#2 T 扩展”。

    现在您的示例更具可读性:

    “不可能有损坏。对 setFirst 的调用是一个类型错误。要了解原因,让我们仔细看看类型 Pair&lt; some CAP#1 extending Employee &gt;。它的方法如下所示:

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

    由于CAP#1 是一种虚构的类型,因此您不能使用Employee 调用setFirst(),因为您无法知道CAP#1Employee 还是它的某个随机子类型。 setFirst() 是一个逆变方法;它使用泛型类型的实例。

    但是,无论CAP#1 是什么,它都会扩展Employee,因此您可以调用getFirst() 并将结果分配给Employee 类型的变量。 getFirst()协变的;它产生泛型类型的实例。

    所以你问,如果你不能在这样的变量上使用一半的方法,你到底为什么想要Pair&lt; ? extends Employee &gt; 类型的变量?

    因为您可以对通配符类型做的一件事是解决 Java 中的一些差异问题。无论我有Pair&lt; Employee &gt;Pair&lt; Manager &gt; 还是Pair&lt; SomeClassExtendingManager &gt;,我都可以将它分配给Pair&lt; ? extends Employee &gt; 类型的变量。我无法将Pair&lt; Manager &gt; 分配给Pair&lt; Employee &gt; 类型的变量,即使Pair 中没有逆变方法,因为类型系统无法解决这个问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多