我已经阅读了许多线程来解释接口和抽象类之间的差异;这些帖子特别有用:What is the difference between an interface and abstract class?和Interface vs Abstract Class (general 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关系,但接口根本不是类,所以继承仍适用于它们吗? 实现多个接口的类是否与这些接口具有ISA关系?
这个问题可以进一步抽象为:为什么接口的引用变量可以实现该接口的类?
答案 0 :(得分:4)
您的Animal[]
持有Animals
,对。
但是您当然知道Dog
也是Animal
,因此编译器可以将其视为Animal
,并将其作为Animal[]
添加到数组中。
当然,当你浏览数组时,编译器不能进一步假设哪个引用属于哪个类,所以只有接口方法在你的循环中可用。
多态的好处。
编辑:也许您的问题更关心类与界面的差异,您想知道如何存储对界面实例的引用?
即使之前的措辞也很粗糙。没有接口实例,而是接口实现的实例。那么你的Dog
实际拥有的是对你实现该接口的任何类的实例的引用(即Fish
,NyanCat
,String
或你有什么。)
另请注意,在之前的解释中,我并不真正关心类与界面的差异。编译器也没有。所有它关心的是正确输入,也就是说,您实际上无法向Animal[]
添加Animal
,但只有衍生物/子项-types {{1}}。
强类型。