Java面向对象之接口(二)
我们之前谈到,Java作为面向对象的语言,并不支持多继承。但是Java中的接口可以实现多继承即会出现:interface A extends InterfaceC,InterfaceD的形式,在一定程度上增加了灵活性。不仅这样,Java中的类也可以实现(implements)多个接口,间接地达到所继承的功能,成为“多实现”。
其实,当我们了解了接口,会发现这完全是合理的。为什么这么说呢?在Java中如果你继承两个类,两个类中有同名但不同行为的方法,你在派生类中是不是不知道用哪个,是不是很头疼。但是接口就不一样,我们知道,接口中的抽象方法或者默认方法最终会被实现类定义(如果两个方法方法签名相同但是返回类型不同,这时在实现方法时就需要返回两者的公共子类型)所以不存在选择上的困难。接下来,我们慢慢来分析接口继承中可能会出现的问题。
接口定义与实现接口
extends关键字的出现一定代表着一种继承,或者说是一种扩展,就是子接口可以获得父接口中的成员。注意:接口可以继承接口,但是不能够继承类哦。
interface Countable {
int NUM = 50;
}
interface Edible {
String name = "Edible";
void howToEat();
}
interface CreatureDoing extends Edible, Countable {
public abstract void howToSleep();
}
这时让CreatureDoing接口继承Edible和Countable两个接口,用逗号隔开,可以验证,子接口中获得了父接口的成员。
System.out.println(CreatureDoing.name);//Edible
System.out.println(CreatureDoing.NUM);//50
接口无法用于创建实例,所以接口需要被类实现,这时就需要用到implements关键字。格式如下:
//类实现多个接口
class 类 implements 接口1,接口2{}
//类继承某类,且实现多个接口
class 类1 extends 类2 implements 接口1,接口2{}
接口的实现和继承类似,可以获得接口中的成员变量和方法,但是需要注意的是,实现类中需要明确给出所实现的接口的所有抽象方法,否则该实现类需要定义为抽象类。这句话,有点绕,我们知道接口中的抽象方法是需要实现类去实现的,我们又说了接口可以继承接口,类可以实现多个接口。那么,如果接口A中有一个抽象方法method1,接口B继承接口A,有一个抽象方method2,类C继承了这两个接口,就要求C在类中实现这两个抽象方法,这就叫实现所有的抽象方法。
还有一个点就是,实现方法其实就是重写接口中的抽象方法啦,所以必须要声明实现方法为public的,不然不满足重写的要求呢。
接口与多态
可以把接口当作一种引用数据类型,可以直接用接口调用其成员,如上Creature.NUM。
不仅如此,接口可以声明引用类型的变量指向其实现类的对象,这类似于让父类变量指向子类对象,是多态性的很好体现。
//接口也实现了多态
Edible[] ediblesArray = new Edible[]{new Chicken(), new Orange()};
for (Edible edible : ediblesArray) {
edible.howToEat();
}
接口是否继承于Object
在书上看到一句话:所有的接口类型的引用变量都可以直接赋值给Object类型的变量,于是我产生疑惑,既然这样是不是意味着接口类型也是继承于Object类呢?
于是我进行了尝试,我用接口类型的变量调用了Object类的方法:
System.out.println(cd.hashCode());
让我震惊的是,可以调用成功。但是我始终心存疑惑,于是我展开了漫长的资料搜索,网络上的资料参差不齐,最终在这里贴一下我在stack overflow上看到的觉得比较靠谱的答案:Do interfaces inherit from Object class in java
接口和类始终是并行的关系,它们都代表着一种引用数据类型,所有类都继承于Obejct类,但接口并不是。接口类型变量之所以能够调用Object类中的方法,是因为接口中隐含了一套和Object类方法签名完全相同的方法,如果不这样子的话,不能够编译成功。
而接口类型的引用变量可以直接赋值给Object类型的变量,也可以这么理解:接口最终都是需要实现类去实现的,引用变量指向实现类的实例,而实现类都是Object的子类,所以是完全合理的。
package com.my.pac19;
/**
* @auther Summerday
*/
public class TestEdible {
public static void main(String[] args) {
System.out.println(Countable.NUM);
System.out.println(Edible.name);
//接口 不能用于创建实例,但是可以声明引用类型变量指向其实现类的对象
CreatureDoing cd = new Chicken();
System.out.println(cd.hashCode());
System.out.println();
//接口也实现了多态
Edible[] ediblesArray = new Edible[]{new Chicken(), new Orange()};
for (Edible edible : ediblesArray) {
edible.howToEat();
}
}
}
interface Countable {
int NUM = 50;
}
interface Edible {
String name = "Edible";
public abstract void howToEat();
//redundant 多余的
}
//接口继承接口
interface CreatureDoing extends Edible, Countable {
public abstract void howToSleep();
}
abstract class Animal {
public abstract void call();
}
class Chicken extends Animal implements Edible, CreatureDoing {
//实现Edible中的howToEat方法
@Override
public void howToEat() {
System.out.println("鸡要烤着吃");
}
//实现Animal中的call方法
@Override
public void call() {
System.out.println("咯咯哒");
}
//实现Creature中的howToSleep方法
@Override
public void howToSleep() {
System.out.println("呼呼");
}
}
class Orange implements Edible {
@Override
public void howToEat() {
System.out.println("橘子要剥皮吃");
}
}
本文若有叙述不当之处,欢迎评论区留言交流!