首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >仿制药..?超级T

仿制药..?超级T
EN

Stack Overflow用户
提问于 2009-02-26 15:25:14
回答 7查看 19.9K关注 0票数 13

可能重复: Java泛型中的“超级”和“扩展”有什么区别?

a)

代码语言:javascript
复制
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) { ... }

当我只能添加形状及其导数时,为什么迭代必须是对象的呢?

b)

代码语言:javascript
复制
List<? super Shape> shapeSuper = new ArrayList<Object>();  

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

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

EN

回答 7

Stack Overflow用户

发布于 2009-02-26 23:22:19

对于您的示例,可以像Dan和plain那样使用普通的List<Shape>;您不需要使用通配符问号语法,比如List<? super Shape>List<? extends Shape>)。我想你的基本问题可能是,“我什么时候会使用问号样式声明之一?”( Julien引用的Get和Put原则是对这个问题的一个很好的回答,但我认为它没有多大意义,除非你在一个例子中看到它。)下面是关于何时使用通配符的Get和Put原则的扩展版本。

如果.使用<? extends T>

  • 具有泛型类参数Foo<T> readSource的方法。
  • 该方法从readSource获取T的实例,而不关心检索到的实际对象是否属于T的子类。

如果.使用<? super T>

  • 具有泛型类参数Foo<T> writeDest的方法。
  • 该方法将T的实例放入writeDest中,而不关心writeDest是否也包含T的子类对象。

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

代码语言:javascript
复制
void processSquare(List<Square> iSqua, List<Square> oSqua)
{ Square s = iSqua.remove(0); s.doSquare(); oSqua.add(s); }

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

代码语言:javascript
复制
List<DoubleSquare> dsqares = ... 
List<Square> processed = new ArrayList<Square>;
processSquare(dsqares, processed); // compiler error! dsquares is not List<Square>

编译器出错,因为dsquare List<DoubleSquare>的类型与processSquare,List<Square>的第一个参数的类型不匹配。也许DoubleSquare是一个正方形,但是为了您的processSquare方法,您需要告诉编译器List<DoubleSquare>是一个List<Square><? extends Square> 使用通配符告诉编译器,您的方法可以接受Square.的任何子类的列表。

代码语言:javascript
复制
void processSquare(List<? extends Square> iSqua, List<Square> oSqua)

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

代码语言:javascript
复制
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> 使用通配符告诉编译器,给定的参数可以是任何超类 of Square.的列表。

代码语言:javascript
复制
void processSquare(List<? extends Square> iSqua, 
                          List<? super Square> oSqua) 

下面是该示例的完整源代码。有时,我发现通过从一个工作示例开始,然后打破它来查看编译器是如何反应的,学习东西就更容易了。

代码语言:javascript
复制
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; 
  }

}
票数 30
EN

Stack Overflow用户

发布于 2009-02-26 15:38:21

要在保罗的回答上扩展,将shapeSuper声明为List<?SuperShape>,您是说它可以接受任何形状超类的对象。对象是形状的超类。这意味着每个列表元素的公共超类都是Object。

这就是为什么必须在for循环中使用对象类型的原因。就编译器而言,列表可能包含非形状的对象。

票数 23
EN

Stack Overflow用户

发布于 2009-02-26 17:06:02

第(2.4)节中的"Get和Put原则“是来自Java泛型和集合的真正的宝石。

Get和put原则:只从结构中获取值时使用扩展通配符,只将值放入结构时使用超级通配符,在获得和放置值时不要使用通配符。

同时,将一个类型声明为List<? super Shape> shapeSuper也是一种糟糕的形式,因为它限制了它的使用。一般来说,我唯一使用外卡的时候是方法签名:

代码语言:javascript
复制
public void foo(List<? super Shape> shapeSuper)
票数 18
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/591004

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档