多重继承的问题及接口的引入
我们不难发现,在自然界中,很多时候父子关系并不是很显然的树型,而是图型。打个比方,机器狗应该从哪种对象继承呢?机器还是狗?
对于这种问题,C++提供了多重继承的概念,即一个类可以拥有多个父类。但很显然,这种情况虽然方便,但也带来了很多问题,例如不同父类的同名属性/方法的混淆,以及对共同父类多次调用带来的资源浪费(钻石继承问题)。
因此, Java 引入了接口的概念。
接口的实现
public interface AD {
//物理伤害
public void physicAttack();
}
public class ADHero extends Hero implements AD{
@Override
public void physicAttack() {
System.out.println("进行物理攻击");
}
}
ADHero 的父类是 Hero,但是他同时引用了接口 AD。
接口 AD 中有一个方法,但是这个方法是空的(很像抽象方法,是不是?),这依赖于引用它的类来具体实现。
注意,在 ADHero 中必须重写(下面会细讲)这个接口提供的方法,不然会报错。
接口的意义
接口可以理解为一种规范,一旦你引入了这个规范,你便必须遵守这个规范(完整的对规范提供的方法进行实现);与此同时,你实现了规范以后,也可以在其他遵守该规范的程序进行交互。
打个比方,我们对于一个类,需要实现大小比较的功能,但是 Java 不像 C++ ,并没有运算符重载的功能。幸运的是,我们可以采用官方库提供的一个接口,来实现比较功能。实现之后,我们不但可以进行类的比较,而且可以用于众多基于比较来实现的官方库,例如各种有序集合框架,排序,最大最小值等等。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
class Student implements Comparable<Student> { //引用官方的Comparable接口实现大小比较
String name;
int id, score, cnt;
Student() {
name = "";
score = 0;
cnt = 0;
}
//重写compareTo方法
@Override
public int compareTo(Student s) {
if (score * s.cnt > s.score * cnt) return -1;
else if (score * s.cnt < s.score * cnt) return 1;
else {
if (id > s.id) return 1;
else if (id < s.id) return -1;
else return 0;
}
}
}
public class Main {
public static void main(String []args) {
//新建一个ArrayList
ArrayList<Student> stuList = new ArrayList<Student>();
//一堆向ArrayList里面添加东西的操作
//实现好接口,我们可以直接调用官方写好的排序,就不用我们自己写了
Collections.sort(stuList);
}
}
默认方法
含义
从 JDK8 开始,接口里面的方法可以提供具体内容了,而非以前那样只能空着。如果一个类引用了接口但是没有重写该方法,那么调用的时候就会使用默认方法。
interface animal {
//传统,只提供定义,但不负责实现
public void eat();
public void sleep();
//新版JDK,给出定义的同时还给出了默认实现
default public void run() {
System.out.println("I am running!");
}
}
优点
打个比方,我有一个接口很受欢迎,我自己写的大批 Java 程序都用了这个接口。
有一天,我突然打算更新它,在里面加上新方法。好家伙,难道我要一个个找到那些使用了它的类,然后一个个补上去吗?
这时候,有了默认方法,那么我们的原来程序依旧可以正常运行,不需要一个个给他们编写新方法的实现了。
缺点(我自己感觉的)
我的一个类调用了一个接口,因为代码太多,我忘了写好具体实现了。
搁以前的话,编译器会立刻报错,提醒我忘了重写接口的方法了。但是现在有了默认方法,很多接口都会提供,这样的话编译器就不会报错,正常运行,空留着我看着匪夷所思的运行结果,一脸茫然……为了避免这种情况,还是把编译器的编译警报等级调到最高吧(逃