【问题标题】:Can all wildcards in Java be replaced by non-wildcard types?Java中的所有通配符都可以用非通配符类型代替吗?
【发布时间】:2011-09-06 08:47:38
【问题描述】:

我找不到任何不能用泛型替换通配符的示例。 例如:

 public void dummy(List<? extends MyObject> list);

等价于

 public <T> void dummy(List<T extends MyObject> list);

 public <T> List<? extends T> dummy2(List<? extends T> list);

等价于

 public <T, U>  List<U extends T> dummy(List<U extends T> list);

所以我不明白为什么创建通配符,因为泛型已经在做这项工作。有什么想法或cmets?

【问题讨论】:

    标签: java generics wildcard


    【解决方案1】:

    不,它并不总是可替换的。

    List<? extends Reader> foo();
    

    不等于

    <T> List<T extends Reader> foo();
    

    因为您在调用foo() 时不知道T(并且您不知道List&lt;T&gt;foo() 返回什么。同样的事情也发生在您的第二个示例中。

    demonstration of using wildcards can be found in this (my) answer

    【讨论】:

    • 我忘了!无论如何,我关于Angelika Lanker's FAQ 的评论是正确的:-)
    • 从调用者的角度来看,foo1()只能分配给List&lt;? extends Reader&gt; x = foo1()。此类用法可以替换为List&lt;? extends Reader&gt; x = foo2()。因此foo1 可以替换为foo2(反过来不成立)。在方法体中,foo2 并不能真正利用T,所以 foo1 和 foo2 impl 基本相同。
    • @irreputable:第一点是对的,foo2可以代替foo1。但是,它们的实现可能性有很大的不同,因为 foo1 可以选择列表元素的类型。超过一千字的例子:ideone.com/YKLum
    【解决方案2】:

    一个简单的答案是,更深层次的通配符不能被类型变量替换

    void foo( List<List<?>> arg )
    

    很不一样
    <T> 
    void foo( List<List<T>> arg)
    

    这是因为通配符捕获转换仅适用于 1 级通配符。让我们谈谈这些。

    由于广泛的捕获转换,在大多数情况下,编译器将通配符视为类型变量。因此,程序员确实可以在这些地方用类型变量替换通配符,一种手动捕获转换。

    由于程序员无法访问编译器为捕获转换创建的类型变量,这具有@josefx 提到的限制效果。例如,编译器将List&lt;?&gt; 对象视为List&lt;W&gt; 对象;因为W是编译器内部的,虽然它有一个方法add(W item),但程序员没有办法调用它,因为他没有W类型的项目。但是,如果程序员“手动”将通配符转换为类型变量T,他可以拥有一个类型为T 的项目并在其上调用add(T item)

    另一种相当随机的情况,通配符不能替换类型变量:

    class Base
        List<? extends Number> foo()
    
    class Derived extends Base
        List<Integer> foo()
    

    这里,foo() 被一个协变返回类型覆盖,因为List&lt;Integer&gt;List&lt;? extends Number 的子类型。如果将通配符替换为类型变量,这将不起作用。

    【讨论】:

      【解决方案3】:

      您的示例在用法上有所不同。

      public <T> List<? extends T> dummy2(List<? extends T> list);
      

      返回一个可以包含 T 的未知子类型的列表,因此您可以从中获取 T 类型的对象,但不能添加到其中。

      示例 T = 数字

      List<? extends Number> l = new ArrayList<Integer>(Arrays.asList(new Integer(1)));
      //Valid since the values are guaranteed to be a subtype of Number
      Number o = l.get(0);
      //Invalid since we don't know that the valuetype of the list is Integer
      l.add(new Integer(1));
      

      所以通配符可以用来使某些操作无效,这可以被API用来限制接口。

      例子:

      //Use to remove unliked numbers, thanks to the wildcard 
      //it is impossible to add a Number
      @Override public void removeNumberCallback(List<? extends Number> list){
          list.remove(13);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-09-23
        相关资源
        最近更新 更多