为什么对接口的引用数组可以包含实现该接口的类?

我已经阅读了许multithreading来解释接口和抽象类之间的差异; 这些post特别有用: 接口和抽象类有什么区别? 和接口与抽象类(通用OO) ; 但是,我找不到任何专门解决我问题的答案。

上下文:今天在课堂上我们编写了一个接口和两个实现接口的类:

public interface Animal{ // This is in its own file void move(); void makeSound(); } class Dog implements Animal{ // The rest of these are in another file public void move(){ System.out.println("Runs."); } public void makeSound(){ System.out.println("Barks."); } } class Fish implements Animal{ public void move(){ System.out.println("Swims."); } public void makeSound(){ System.out.println("Bloops."); } } public class Zookeeper{ public static void main(String[] args){ Animal[] zoo = new Animal[10]; // This an array of references to an interface for(int i = 0; i < zoo.length; i++){ zoo[i] = (Math.round(Math.random()) == 0)?new Dog():new Fish(); } for(Animal animal: zoo){ System.out.println(animal.getClass().getName()); animal.makeSound(); animal.move(); } } } 

我知道这是有可能的,因为它有效; 我的问题是为什么它有效? 我得到的子类与它们的超类有一个ISA关系,但是接口根本不是类,所以inheritance仍适用于它们吗? 实现多个接口的类与这些接口有ISA关系吗?

这个问题可以进一步抽象为:为什么接口的引用变量可以实现该接口?

你的Animal[]拥有Animals ,对。

但是当然你知道Dog也是一个Animal ,因此它可以被编译器认为是这样并作为Animal添加到你的数组中。

当然,当您浏览数组时,编译器无法进一步假设哪个引用属于哪个类, 因此只有接口方法可用于您的循环中

多态性的一个很好的观点。

编辑 :也许您的问题更关心类与接口的差异,您想知道如何存储对接口实例的引用?

甚至以前的措辞也很粗糙。 没有接口实例 ,而是接口实现的实例。 那么你的Animal[]实际上持有的是对你实现该接口的任何类的实例引用(即DogFishNyanCat或者你有什么)。

另请注意,在之前的解释中,我并不真正关心类与接口的差异。 编译器也不是。 所有它关心的是正确的输入 ,也就是说,你实际上不能将一个String添加到你的Animal[] ,例如,只有Animal衍生物/子类型

强烈打字的要点