当前位置:  开发笔记 > 编程语言 > 正文

泛型..?超级T

如何解决《泛型..?超级T》经验,为你挑选了3个好方法。

对于你的例子,你可以使用List像丹和保罗所说的平原; 您不需要使用通配符问号语法,例如ListList).我认为您的基本问题可能是"我何时会使用其中一种问号样式声明?" (Julien引用的Get和Put原则是这个问题的一个很好的答案,但我认为除非你在一个例子的上下文中看到它,否则它没有多大意义.)这是我对Get和扩展版本的看法. Put原则何时使用通配符.

使用如果......

方法具有泛型类参数FooreadSource

方法GETS来自readSource的T实例,并不关心检索的实际对象是否属于T的子类.

使用如果......

方法具有泛型类参数FoowriteDest

方法PUTS将T实例写入writeDest,并不关心writeDest是否还包含作为T子类的对象.

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

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

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

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

编译器因错误而失败,因为dsquares List的类型与processSquare的第一个参数的类型不匹配List.也许DoubleSquare是一个Square,但是你需要告诉编译器a Listis-a List用于processSquare方法.使用通配符告诉编译器您的方法可以获取Square的任何子类的List.

void processSquare(List iSqua, List oSqua)

接下来,您将改进应用程序以处理Circles和Squares.您希望将所有已处理的形状聚合在一个包含圆形和方形的列表中,因此您将已处理列表的类型从a List更改为List:

List dsqares = ... 
List circles = ... 
List processed = new ArrayList;
processSquare(dsqares, processed); // compiler error! processed is not List

编译器失败并出现新错误.现在处理列表的类型List与processSquare的第二个参数不匹配List.使用通配符告诉编译器给定参数可以是任何超类 Square 的List .

void processSquare(List iSqua, 
                          List oSqua) 

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

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 iSqua, List oSqua) 
  { Square s = iSqua.remove(0); s.doSquare(); oSqua.add(s); }

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

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

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

    // 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  List makeList(T a) { 
    List list = new LinkedList(); list.add(a); return list; 
  }

}

不同意长篇文章.当然,我花了几分钟阅读和消化,但我很感激详细而实用的解释.我现在比以前更了解.如果读者不准备花几分钟来理解一些相当复杂的规则,那么他们也可能不会费心去理解它. (3认同)

好解释. (2认同)


Dan Dyer.. 23

通过将shapeSuper声明为List <?来扩展Paul的答案.super Shape>,你说它可以接受任何超级Shape的对象.对象是形状的超类.这意味着每个列表元素的公共超类是Object.

这就是你必须在for循环中使用Object类型的原因.就编译器而言,列表可能包含不是形状的对象.



1> ThisIsTheDav..:

对于你的例子,你可以使用List像丹和保罗所说的平原; 您不需要使用通配符问号语法,例如ListList).我认为您的基本问题可能是"我何时会使用其中一种问号样式声明?" (Julien引用的Get和Put原则是这个问题的一个很好的答案,但我认为除非你在一个例子的上下文中看到它,否则它没有多大意义.)这是我对Get和扩展版本的看法. Put原则何时使用通配符.

使用如果......

方法具有泛型类参数FooreadSource

方法GETS来自readSource的T实例,并不关心检索的实际对象是否属于T的子类.

使用如果......

方法具有泛型类参数FoowriteDest

方法PUTS将T实例写入writeDest,并不关心writeDest是否还包含作为T子类的对象.

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

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

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

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

编译器因错误而失败,因为dsquares List的类型与processSquare的第一个参数的类型不匹配List.也许DoubleSquare是一个Square,但是你需要告诉编译器a Listis-a List用于processSquare方法.使用通配符告诉编译器您的方法可以获取Square的任何子类的List.

void processSquare(List iSqua, List oSqua)

接下来,您将改进应用程序以处理Circles和Squares.您希望将所有已处理的形状聚合在一个包含圆形和方形的列表中,因此您将已处理列表的类型从a List更改为List:

List dsqares = ... 
List circles = ... 
List processed = new ArrayList;
processSquare(dsqares, processed); // compiler error! processed is not List

编译器失败并出现新错误.现在处理列表的类型List与processSquare的第二个参数不匹配List.使用通配符告诉编译器给定参数可以是任何超类 Square 的List .

void processSquare(List iSqua, 
                          List oSqua) 

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

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 iSqua, List oSqua) 
  { Square s = iSqua.remove(0); s.doSquare(); oSqua.add(s); }

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

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

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

    // 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  List makeList(T a) { 
    List list = new LinkedList(); list.add(a); return list; 
  }

}


不同意长篇文章.当然,我花了几分钟阅读和消化,但我很感激详细而实用的解释.我现在比以前更了解.如果读者不准备花几分钟来理解一些相当复杂的规则,那么他们也可能不会费心去理解它.
好解释.

2> Dan Dyer..:

通过将shapeSuper声明为List <?来扩展Paul的答案.super Shape>,你说它可以接受任何超级Shape的对象.对象是形状的超类.这意味着每个列表元素的公共超类是Object.

这就是你必须在for循环中使用Object类型的原因.就编译器而言,列表可能包含不是形状的对象.


+1实际解释什么是错的.添加关于"?super T"与"?extends T"的讨论,我将再次提升.等一下...

3> Julien Chast..:

第(2.4)节中的"获取和放置原则"是来自Java Generics和Collections的真正的宝石:

获取和放置原则:当您只从结构中获取值时使用扩展通配符,当您仅将值放入结构时使用超级通配符,并且当您同时获取和放置时不使用通配符.

替代文字

此外,将类型声明List shapeSuper为一种不良形式,因为它限制了它的使用.通常,我使用通配符的唯一方法是方法签名:

public void foo(List shapeSuper)


这也称为PECS原则(Effective Java 2nd Ed.)."制作人扩展,消费者超级".如果您接受的参数是生产者,请使用"extends".
推荐阅读
个性2402852463
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有