是列表<;狗>;List的一个子类<;动物>;?为什么Java泛型不是隐式多态的?

是列表<;狗>;List的一个子类<;动物>;?为什么Java泛型不是隐式多态的?,java,generics,inheritance,polymorphism,Java,Generics,Inheritance,Polymorphism,我对Java泛型如何处理继承/多态性有点困惑 假设以下层次结构- 动物(父母) 狗-猫(儿童) 因此,假设我有一个方法doSomething(列出动物)。根据继承和多态性的所有规则,我假设列表是列表,列表是列表,因此任何一个都可以传递给这个方法。不是这样。如果我想实现这种行为,我必须通过说doSomething来明确告诉方法接受动物任何子类的列表(列表< P>不,一个代码>清单不是列表>代码>考虑你可以用列表>代码>你可以添加任何动物……包括一只猫。现在,你能在逻辑上向一窝小狗添加一只猫吗?绝对

我对Java泛型如何处理继承/多态性有点困惑

假设以下层次结构-

动物(父母)

-(儿童)

因此,假设我有一个方法
doSomething(列出动物)
。根据继承和多态性的所有规则,我假设
列表
列表
列表
列表
,因此任何一个都可以传递给这个方法。不是这样。如果我想实现这种行为,我必须通过说
doSomething来明确告诉方法接受动物任何子类的列表(列表< P>不,一个代码>清单<代码>不是<代码>列表>代码>考虑你可以用<代码>列表>代码>你可以添加任何动物……包括一只猫。现在,你能在逻辑上向一窝小狗添加一只猫吗?绝对不会。

// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?
//非法代码-否则生活会很糟糕
List dogs=new ArrayList();//ArrayList实现列表
列出动物=狗;//Awooga Awooga
添加(新的Cat());
Dog Dog=dogs.get(0);//这应该是安全的,对吗?
突然你有一只非常困惑的猫


现在,您不能将
Cat
添加到
列表中,您要查找的是所谓的参数。这意味着,如果一种类型的对象可以在方法中替换为另一种类型的对象(例如,
Animal
可以替换为
Dog
),则使用这些对象的表达式也是如此(因此,
List
可以替换为
List
)。问题是,通常情况下,协方差对于可变列表是不安全的。假设您有一个
列表
,并且它被用作
列表
。当您尝试将一个Cat添加到该
列表
(实际上是一个
列表
)时会发生什么?自动允许类型参数协方差会破坏类型系统


添加语法以允许将类型参数指定为协变,这将有助于避免方法声明中的
?extends Foo
,但这确实增加了额外的复杂性。

原因是
列表不是
列表,并且对于为什么有些东西是或不是polymor有一个非常好的、深入的解释PHIC或允许泛型。

< P>我认为泛型的全部要点是它不允许。考虑数组的情况,允许协方差类型:

  Object[] objects = new String[10];
  objects[0] = Boolean.FALSE;
这段代码可以很好地编译,但会引发运行时错误(
java.lang.ArrayStoreException:java.lang.Boolean
,在第二行)。它不是类型安全的。泛型的要点是添加编译时类型安全性,否则您可以只使用没有泛型的普通类


现在有些时候,您需要更加灵活,这就是
?超级类
?扩展类
的用途。前者是您需要插入类型
集合
(例如),后者是在您需要以类型安全的方式从中读取时使用的。但同时执行这两项操作的唯一方法是使用特定的类型。

这种行为的基本逻辑是
泛型
遵循类型擦除机制。因此,在运行时,您无法识别
集合
的类型de>数组
中没有这样的擦除过程。所以回到您的问题

因此,假设有一种方法,如下所示:

add(List<Animal>){
    //You can add List<Dog or List<Cat> and this will compile as per rules of polymorphism
}
添加(列表){
//您可以添加列表我认为应该添加的一点

列表
不是Java中的
列表

这也是事实

狗的名单是英文的动物名单(根据合理解释)

OP直觉的工作方式——这当然是完全有效的——是后一句话。然而,如果我们应用这种直觉,我们会得到一种在其类型系统中不是Java风格的语言:假设我们的语言允许在我们的狗列表中添加一只猫。这意味着什么?这意味着列表不再是狗列表,而rem这仅仅是一份动物名单,一份哺乳动物名单,一份四足动物名单

换句话说:Java中的
列表
在英语中并不意味着“狗的列表”,它的意思是“一个可以有狗的列表,其他什么都没有”

更一般地说,OP的直觉倾向于一种语言,在这种语言中,对对象的操作可以改变它们的类型,或者更确切地说,对象的类型是动态的函数的值。

以及其他答案都是正确的。我将用一个我认为有用的解决方案来补充这些答案。我认为这在编程中经常出现。需要注意的一点是,对于集合(列表、集合等)主要问题是添加到集合中。这就是问题的症结所在。即使删除也可以


在大多数情况下,我们可以使用
Collection这里给出的答案并不能完全说服我。因此,我举了另一个例子

public void passOn(Consumer<Animal> consumer, Supplier<Animal> supplier) {
    consumer.accept(supplier.get());
}
确保我们只能使用为消费者提供正确对象类型的供应商

奥托,我们也可以这么做

public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<A> supplier) {
    consumer.accept(supplier.get());
}

其中,有了直观的关系
生命
->
动物
->
哺乳动物
->
等,我们甚至可以将
哺乳动物
放入
生命
消费者,但不能将
字符串
放入
生命
消费者。

实际上,你可以使用一个界面来实现你的目标需要

public interface Animal {
    String getName();
    String getVoice();
}
public class Dog implements Animal{
    @Override 
    String getName(){return "Dog";}
    @Override
    String getVoice(){return "woof!";}
}

然后,您可以使用

List <Animal> animalGroup = new ArrayList<Animal>();
animalGroup.add(new Dog());
List animalGroup=new ArrayList();
添加(新狗());

如果您确定列表项是给定超类型的子类,则可以使用以下方法强制转换列表:

(List<Animal>) (List<?>) dogs
(列表)(列表)狗
这是有用的,当你
public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<? extends A> supplier) {
    consumer.accept(supplier.get());
}
public interface Animal {
    String getName();
    String getVoice();
}
public class Dog implements Animal{
    @Override 
    String getName(){return "Dog";}
    @Override
    String getVoice(){return "woof!";}
List <Animal> animalGroup = new ArrayList<Animal>();
animalGroup.add(new Dog());
(List<Animal>) (List<?>) dogs
public abstract class Shape {
    public abstract void draw(Canvas c);
}

public class Circle extends Shape {
    private int x, y, radius;
    public void draw(Canvas c) {
        ...
    }
}

public class Rectangle extends Shape {
    private int x, y, width, height;
    public void draw(Canvas c) {
        ...
    }
}
// drawAll method call
drawAll(circleList);


public void drawAll(List<Shape> shapes) {
   shapes.add(new Rectangle());    
}
    // All compiles but throws ArrayStoreException at runtime at last line
    Dog[] dogs = new Dog[10];
    Animal[] animals = dogs; // compiles
    animals[0] = new Cat(); // throws ArrayStoreException at runtime
    List<Dog> dogs = new ArrayList<>();
    List<Animal> animals = dogs; // compile-time error, otherwise heap pollution
    animals.add(new Cat());
<T extends Animal> void doSomething<List<T> animals) {
}
List<Dog> dogs = new ArrayList<Dog>(1);
List<Animal> animals = dogs;
animals.add(new Cat()); // compile-time error
Dog dog = dogs.get(0);
Dog[] dogs = new Dog[1];
Object[] animals = dogs;
animals[0] = new Cat(); // run-time error
Dog dog = dogs[0];
import java.util.ArrayList;
import java.util.List;

public class Demonstration {
    public void normal() {
        List normal = new ArrayList(1);
        normal.add("lorem ipsum");
    }

    public void parameterized() {
        List<String> parameterized = new ArrayList<>(1);
        parameterized.add("lorem ipsum");
    }
}
Compiled from "Demonstration.java"
public class Demonstration {
  public Demonstration();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void normal();
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: iconst_1
       5: invokespecial #3                  // Method java/util/ArrayList."<init>":(I)V
       8: astore_1
       9: aload_1
      10: ldc           #4                  // String lorem ipsum
      12: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      17: pop
      18: return

  public void parameterized();
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: iconst_1
       5: invokespecial #3                  // Method java/util/ArrayList."<init>":(I)V
       8: astore_1
       9: aload_1
      10: ldc           #4                  // String lorem ipsum
      12: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      17: pop
      18: return
}
List<Dog> dogs = new ArrayList<Dog>(); 
List<Animal> animals = new ArrayList<Animal>(dogs);
animals.add(new Cat());
// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?
// This code is fine
List<Dog> dogs = new ArrayList<Dog>();
dogs.add(new Dog());
List<Animal> animals = new ArrayList<>(dogs); // Copy list
animals.add(new Cat());
Dog dog = dogs.get(0);   // This is fine now, because it does not return the Cat
// These are both illegal
dogs = animals;
cats = animals;
abstract class Animal {
    void eat() {
        System.out.println("animal eating");
    }
}

class Dog extends Animal {
    void bark() { }
}

class Cat extends Animal {
    void meow() { }
}
class TestAnimals {
    public static void main(String[] args) {
        Animal[] animals = {new Dog(), new Cat(), new Dog()};
        Dog[] dogs = {new Dog(), new Dog(), new Dog()};
        takeAnimals(animals);
        takeAnimals(dogs);
    }

    public void takeAnimals(Animal[] animals) {
        for(Animal a : animals) {
            System.out.println(a.eat());
        }
    }   
}
animal eating
animal eating
animal eating
animal eating
animal eating
animal eating
class TestAnimals {
    public static void main(String[] args) {
        ArrayList<Animal> animals = new ArrayList<Animal>();
        animals.add(new Dog());
        animals.add(new Cat());
        animals.add(new Dog());
        takeAnimals(animals);
    }

    public void takeAnimals(ArrayList<Animal> animals) {
        for(Animal a : animals) {
            System.out.println(a.eat());
        }
    }   
}
animal eating
animal eating
animal eating
animal eating
animal eating
animal eating
class TestAnimals {
    public static void main(String[] args) {
        ArrayList<Animal> animals = new ArrayList<Animal>();
        animals.add(new Dog());
        animals.add(new Cat());
        animals.add(new Dog());

        ArrayList<Dog> dogs = new ArrayList<Dog>();
        takeAnimals(animals);
        takeAnimals(dogs);
    }

    public void takeAnimals(ArrayList<Animal> animals) {
        for(Animal a : animals) {
            System.out.println(a.eat());
        }
    }   
}
animals.add(new Cat());