Fork me on GitHub

Java的艺术-类和接口的多继承

此处输入图片的描述
Java也有多继承?来看看JDK8的这个新特性!

接口(Interface)

定义

Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。

特点

(JDK8以前)接口中可以含有 变量和方法。但是要注意,接口中的变量会被隐式地指定为public static final变量(并且只能是public static final变量,用private修饰会报编译错误),而方法会被隐式地指定为public abstract方法且只能是public abstract方法(用其他关键字,比如private、protected、static、 final等修饰会报编译错误)

并且接口中所有的方法不能有具体的实现,也就是说,接口中的方法必须都是抽象方法。从这里可以隐约看出接口和抽象类的区别,接口是一种极度抽象的类型,它比抽象类更加“抽象”,并且一般情况下不在接口中定义变量。

Java8 新特性

但是,自从Java 8发布后,接口中也可以自定义方法了(default method)。这样如果为很多继承了同一个接口的类增加功能,不必对这些类重新设计。

1
2
3
4
5
6
7
8
9
public interface Test {
//default关键字不能省略,否则提示Interface abstract methods cannot have body
default String test(String str){
return str;
}
//默认为public,abstract的
int test2();
//default关键字不能与abstact同时修饰接口方法
}

同时,接口方法也可以被static修饰。

但是这也给Java带来了新问题——Java多继承的冲突

接口中被default与static修饰的方法

  • 非default、static方法不能有实现,否则编译错误:Abstract methods do not specify a body

  • default、static方法必须有具体的实现,否则编译错误:This method requires a body instead of a semicolon

  • 可以拥有多个default方法

  • 可以拥有多个static方法

  • 使用接口中类型时,仅仅需要实现抽象方法,default、static方法不需要强制自己新实现

Java多继承

接口多继承冲突

比如有三个接口,Interface Test1,Interface Test2,Interface Test3。其中,Interface Test3继承自Test1和Test2。

如果Test1和Test2有相同签名的默认(default)方法,并且Test3没有override,则编译会出错。

1
2
3
4
5
6
7
8
9
10
11
12
13
interface Test1 {
default String test(String str){
return str;
}
}

interface Test2 {
default String test(String str){
return str;
}
}
interface Test3 extends Test1,Test2 {
}

此时会报错:

1
multiextends.Test3 inherits unrelated defaults for test(String) from types multiextends.Test1 and multiextends.Test2

需要在子接口中覆盖这个方法,让子类知道默认调用哪个方法:

1
2
3
4
5
interface Test3 extends Test1,Test2 {
default String test(String str){
return str;
}
}

注意判断是否签名相同是根据传入的参数,而不是返回的参数。此时接口会根据传入的参数选择不同的default方法执行。
比如下面的会报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Test1 {

default int test(String str){
return 0;
}
}

interface Test2 {
//传入相同的String,返回不同的类型
default String test(String str){
return str;
}
}
//编译通过,不需要覆盖
interface Test3 extends Test1,Test2 {
}

但是下面的编译通过:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Test1 {
default String test(int a){
return null;
}
}

interface Test2 {
//传入不同类型的参数,返回相同的类型
default String test(String str){
return str;
}
}

//报错,需要覆盖
//interface Test3 extends Test1,Test2 {
//}

超类多继承冲突

如果比如有两个接口,Interface Test1,Interface Test2,一个超类Class Test3,一个子类Class Test4其中,Class Test4实现Interface Test1和Interface Test2,继承Class Test3。

如果方法有冲突会怎么样?
直接看代码:(注意注释中对静态方法和静态属性的 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
interface Test1 {
default String test(int a){
return "test";
}
}

interface Test2 {
//传入不同类型的参数,返回相同的类型
default String test(String str){
return "test2";
}
}

class Test3{
//如果public换成static会报错
//但是如果只和一个Interface冲突,则可以用static修饰。
//因为静态方法和属性可以被继承,但是不能被重写,而是被"隐藏",直接通过父类名称调用。
public String test(String str){
return "test3";
}
}

class Test4 extends Test3 implements Test1,Test2{
public static void main(String[] args) {
Test4 test = new Test4();
System.out.println(test.test("str"));
}
}
//执行结果
//test3

总结

接口冲突:如果一个类同时实现了具有相同方法签名的接口,则该类必须覆盖该抽象方法。

超类冲突:超类和接口提供相同方法签名的方法,此时子类要是不覆盖,则默认调用超类方法。

-------------本文结束感谢阅读-------------