首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >什么是PECS (生产者扩展消费者超级)?

什么是PECS (生产者扩展消费者超级)?
EN

Stack Overflow用户
提问于 2010-04-27 17:16:40
回答 15查看 149.9K关注 0票数 871

我在阅读仿制药的时候遇到了PECS (生产者extends和消费者super的缩写)。

有谁能向我解释一下如何用果胶来解决extendssuper之间的混淆?

EN

回答 15

Stack Overflow用户

回答已采纳

发布于 2010-04-27 17:37:26

博士:的“果胶”是从集合的角度来看的。如果您只是从一个泛型集合中提取项目,那么它是一个生产者,您应该使用extends;如果您只是在填充项目,那么它就是消费者,您应该使用super。如果两者都使用相同的集合,则不应该使用extendssuper

假设您有一个方法,它的参数是一个事物集合,但是您希望它比只接受一个Collection<Thing>更灵活。

案例1:您想要遍历集合并对每一项进行操作。

然后列表是一个生产者,所以您应该使用Collection<? extends Thing>

其理由是,Collection<? extends Thing>可以保存Thing的任何子类型,因此在执行操作时,每个元素都将表现为Thing。(实际上不能向Collection<? extends Thing>添加任何内容(null除外),因为您无法在运行时知道集合中包含哪些特定的Thing子类型。

案例2:您希望向集合中添加内容.

然后列表是一个使用者,所以您应该使用Collection<? super Thing>

这里的理由是,与Collection<? extends Thing>不同的是,无论实际参数化类型是什么,Collection<? super Thing>始终可以持有Thing。在这里,您不关心列表中已经有哪些内容,只要它允许添加一个Thing;这是? super Thing所保证的。

票数 994
EN

Stack Overflow用户

发布于 2013-11-02 06:34:38

这背后的原理在计算机科学被称为

  • 协方差:? extends MyClass
  • 反方差:? super MyClass
  • 不变性/不变性:MyClass

下面的图片应该解释这个概念。图片提供:安德烈·尤金

票数 648
EN

Stack Overflow用户

发布于 2015-07-26 06:33:49

在处理集合时,在上、下有界通配符之间进行选择的一个常见规则是PECS。信用

生产者extends和消费者super

t (extend)和Put (Super)原理。

  • 原则指出:
代码语言:javascript
复制
- Use an `extends` wildcard when you only get values out of a structure.
- Use a `super` wildcard when you only put values into a structure.
- And don’t use a wildcard when you both get and put.

Java中的示例:

代码语言:javascript
复制
class Super {
        Number testCoVariance() {
            return null;
        }
        void testContraVariance(Number parameter) {
        } 
    }
    
    class Sub extends Super {
        @Override
        Integer testCoVariance() {
            return null;
        } //compiles successfully i.e. return type is don't care(Integer is subtype of Number)
        @Override
        void testContraVariance(Integer parameter) {
        } //doesn't support even though Integer is subtype of Number
    }

Liskov替换原则(LSP)指出,“程序中的对象应该与其子类型的实例进行替换,而不改变程序的正确性”。

在编程语言的类型系统中,输入规则。

  • 如果协变量保留类型的排序(≤),则将类型从更特定的排序排序到更一般的排序;
  • contravariant,如果它颠倒了这个顺序;
  • 如果这两种情况都不适用,则不变或非变量。

协方差与反方差

  • 只读数据类型(源)可以是协变
  • 只写数据类型(接收器)可以是contravariant.。
  • 作为源和接收器的可变数据类型应该是不变的

为了说明这种普遍现象,请考虑数组类型。对于类型动物,我们可以使类型Animal[]

  • 协变:A Cat[]是Animal[];
  • contravariant:an Animal[]是Cat[];
  • 不变:Animal[]不是Cat[],Cat[]不是Animal[]。

Java示例:

代码语言:javascript
复制
Object name= new String("prem"); //works
List<Number> numbers = new ArrayList<Integer>();//gets compile time error

Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
myNumber[0] = 3.14; //attempt of heap pollution i.e. at runtime gets java.lang.ArrayStoreException: java.lang.Double(we can fool compiler but not run-time)

List<String> list=new ArrayList<>();
list.add("prem");
List<Object> listObject=list; //Type mismatch: cannot convert from List<String> to List<Object> at Compiletime  

更多的例子

图像src

bounded(i.e.走向某个地方)通配符:有3种不同口味的通配符:

  • 不变/不变异:?? extends Object -无界通配符.它代表着各种类型的家庭。当你们都得到和放置时使用。
  • 协方差:? extends T ( T后裔的统治)--一个具有上界的通配符.T是继承层次结构中的高级-most类。当您只从结构中获取extends值时,请使用extends通配符。
  • 方差:? super T ( T祖先的Reign of T祖先)-具有下限的通配符。T是继承层次结构中的-most类。当您只将super值放入结构中时,请使用super通配符。

注意:通配符?表示0或一次,表示未知类型的。通配符可以用作参数的类型,从未用作泛型方法调用(泛型类实例创建)的类型参数。

代码语言:javascript
复制
 import java.util.ArrayList;
import java.util.List;

class Shape { void draw() {}}

class Circle extends Shape {void draw() {}}

class Square extends Shape {void draw() {}}

class Rectangle extends Shape {void draw() {}}

public class Test {

    public static void main(String[] args) {
        //? extends Shape i.e. can use any sub type of Shape, here Shape is Upper Bound in inheritance hierarchy
        List<? extends Shape> intList5 = new ArrayList<Shape>();
        List<? extends Shape> intList6 = new ArrayList<Cricle>();
        List<? extends Shape> intList7 = new ArrayList<Rectangle>();
        List<? extends Shape> intList9 = new ArrayList<Object>();//ERROR.


        //? super Shape i.e. can use any super type of Shape, here Shape is Lower Bound in inheritance hierarchy
        List<? super Shape> inList5 = new ArrayList<Shape>();
        List<? super Shape> inList6 = new ArrayList<Object>();
        List<? super Shape> inList7 = new ArrayList<Circle>(); //ERROR.

        //-----------------------------------------------------------
        Circle circle = new Circle();
        Shape shape = circle; // OK. Circle IS-A Shape

        List<Circle> circles = new ArrayList<>();
        List<Shape> shapes = circles; // ERROR. List<Circle> is not subtype of List<Shape> even when Circle IS-A Shape

        List<? extends Circle> circles2 = new ArrayList<>();
        List<? extends Shape> shapes2 = circles2; // OK. List<? extends Circle> is subtype of List<? extends Shape>


        //-----------------------------------------------------------
        Shape shape2 = new Shape();
        Circle circle2= (Circle) shape2; // OK. with type casting

        List<Shape> shapes3 = new ArrayList<>();
        List<Circle> circles3 = shapes3; //ERROR. List<Circle> is not subtype of  List<Shape> even Circle is subetype of Shape

        List<? super Shape> shapes4 = new ArrayList<>();
        List<? super Circle> circles4 = shapes4; //OK.
    }

    
    
    /*
     * Example for an upper bound wildcard (Get values i.e Producer `extends`)
     *
     * */
    public void testCoVariance(List<? extends Shape> list) {
        list.add(new Object());//ERROR
        list.add(new Shape()); //ERROR
        list.add(new Circle()); // ERROR
        list.add(new Square()); // ERROR
        list.add(new Rectangle()); // ERROR
        Shape shape= list.get(0);//OK so list act as produces only
    /*
     * You can't add a Shape,Circle,Square,Rectangle to a List<? extends Shape>
     * You can get an object and know that it will be an Shape
     */
    }
    
    
    /*
     * Example for  a lower bound wildcard (Put values i.e Consumer`super`)
     * */
    public void testContraVariance(List<? super Shape> list) {
        list.add(new Object());//ERROR
        list.add(new Shape());//OK
        list.add(new Circle());//OK
        list.add(new Square());//OK
        list.add(new Rectangle());//OK
        Shape shape= list.get(0); // ERROR. Type mismatch, so list acts only as consumer
        Object object= list.get(0); //OK gets an object, but we don't know what kind of Object it is.
        /*
         * You can add a Shape,Circle,Square,Rectangle to a List<? super Shape>
         * You can't get an Shape(but can get Object) and don't know what kind of Shape it is.
         */
    }
}

仿制药示例

协方差与反方差根据类型确定兼容性。在这两种情况下,方差都是有向关系。协方差可以翻译为“在同一方向上不同的”或“有-不同的”,而反方差的意思是“在相反的方向上的不同的”或“相反的-不同的”。协变型和反变型并不相同,但两者之间有一定的相关性。这些名称暗示了相关的方向。

https://stackoverflow.com/a/54576828/1697099

https://stackoverflow.com/a/64888058/1697099

  • 协方差:接受子类型(只读,即生产者)
  • 反方差:接受超级类型(只写,即消费者)
票数 85
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2723397

复制
相关文章

相似问题

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