java - 泛型..? super T

标签 java generics

<分区>

Possible Duplicate:
what is the difference between ‘super’ and ‘extends’ in Java Generics

一个)

List<? super Shape> shapeSuper = new ArrayList<Shape>();

shapeSuper.add(new Square());       //extends from SHAP  
shapeSuper.add(new DoubleSquare()); //extends from SQ  
shapeSuper.add(new TripleSquare()); //extends from DS  
shapeSuper.add(new Rectangle());    //extends from SHAP  
shapeSuper.add(new Circle());       //extends from SHAP  

for (Object object : shapeSuper) { ... }

当我只能添加 Shape 及其对象时,为什么迭代必须是 Objects 衍生品?

B)

List<? super Shape> shapeSuper = new ArrayList<Object>();  

shapeSuper.add(new Object()); //compilation error  

为什么上述行会产生编译错误?

最佳答案

对于您的示例,您可以使用普通的 List<Shape>正如丹和保罗所说;您不需要使用通配符问号语法,例如 List<? super Shape>List<? extends Shape> ).我认为您的潜在问题可能是“我什么时候使用问号样式声明之一?” (Julien 引用的 Get and Put Principle 是这个问题的一个很好的答案,但我认为它没有多大意义,除非你在示例的上下文中看到它。)这是我对 Get and Put 的扩展版本的看法提出何时使用通配符的原则。

使用 <? extends T>如果……

  • 一个方法有一个泛型类 参数Foo<T>阅读来源
  • 方法从 readSource 获取 T 的实例,并且不关心检索到的实际对象是否属于 T 的子类。

使用 <? super T>如果……

  • 一个方法有一个泛型类参数Foo<T>写目标
  • 方法将 T 的实例放入 writeDest,并且不关心 writeDest 是否也包含作为 T 的子类的对象。

下面是一个具体示例的演练,说明了通配符背后的思想。假设您正在编写一个 processSquare 方法,该方法从列表中删除一个正方形,对其进行处理,并将结果存储在输出列表中。这是一个方法签名:

void processSquare(List<Square> iSqua, List<Square> oSqua)
{ Square s = iSqua.remove(0); s.doSquare(); oSqua.add(s); }

现在您创建一个扩展 Square 的 DoubleSquares 列表,并尝试处理它们:

List<DoubleSquare> dsqares = ... 
List<Square> processed = new ArrayList<Square>;
processSquare(dsqares, processed); // compiler error! dsquares is not List<Square>

编译器因错误而失败,因为 dsquares List<DoubleSquare> 的类型与 processSquare 的第一个参数类型不匹配,List<Square> .也许 DoubleSquare 是一个 Square,但是你需要告诉编译器一个 List<DoubleSquare>是一个List<Square>出于您的 processSquare 方法的目的。 使用<? extends Square>通配符告诉编译器您的方法可以采用 Square 的任何子类的列表。

void processSquare(List<? extends Square> iSqua, List<Square> oSqua)

接下来,您将改进应用程序以处理圆形和方形。您希望将所有已处理的形状聚合到一个包含圆形和正方形的列表中,因此您将已处理列表的类型从 List<Square> 更改为到 List<Shape> :

List<DoubleSquare> dsqares = ... 
List<Circle> circles = ... 
List<Shape> processed = new ArrayList<Square>;
processSquare(dsqares, processed); // compiler error! processed is not List<Square>

编译器因新错误而失败。现在处理列表的类型 List<Shape>与 processSquare 的第二个参数不匹配,List<Square> . 使用<? super Square>通配符告诉编译器给定的参数可以是 Square 的任何父类(super class)的列表。

void processSquare(List<? extends Square> iSqua, 
                          List<? super Square> oSqua) 

这是示例的完整源代码。有时我发现从一个工作示例开始学习东西更容易,然后分解它以查看编译器的 react 。

package wild;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public abstract class Main {
  // In processing the square, 
  // I'll take for input  any type of List that can PRODUCE (read) squares.
  // I'll take for output any type of List that can ACCEPT (write) squares.
  static void processSquare(List<? extends Square> iSqua, List<? super Square> oSqua) 
  { Square s = iSqua.remove(0); s.doSquare(); oSqua.add(s); }

  static void processCircle(List<? extends Circle> iCirc, List<? super Circle> oCirc) 
  { Circle c = iCirc.remove(0); c.doCircle(); oCirc.add(c); }

  public static void main(String[] args) {
    // Load some inputs
    List<Circle> circles = makeList(new Circle());
    List<DoubleSquare> dsqares = makeList(new DoubleSquare());

    // Collated storage for completed shapes
    List<Shape> processed = new ArrayList<Shape>();

    // Process the shapes
    processSquare(dsqares, processed);
    processCircle(circles, processed);

    // Do post-processing
    for (Shape s : processed)
      s.shapeDone();
  }

  static class Shape { void shapeDone() { System.out.println("Done with shape."); } }
  static class Square extends Shape { void doSquare() { System.out.println("Square!"); } }
  static class DoubleSquare extends Square {}
  static class Circle extends Shape { void doCircle() { System.out.println("Circle!"); } }

  static <T> List<T> makeList(T a) { 
    List<T> list = new LinkedList<T>(); list.add(a); return list; 
  }

}

关于java - 泛型..? super T,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/591004/

相关文章:

C# 通用序列化实用程序类

spring - 在 Spring MVC 和 Hibernate 中使用通用 DAO 和通用服务模式的优缺点是什么

Java如何设置SSL连接客户端-服务器

java - 获取自己的日、年和月

c# - 确定匿名类型的属性是否为泛型集合

C# 到 VB.Net : Why does this fail to compile when converted to VB?

swift - 通用协议(protocol)方法 swift 无法将类型识别为类型

java - Servlet 中的静态变量

java - 当 n 为偶数时优化 x^n 的递归方法

java - 使用 Spring 注解读取文件属性