java8-新特性-接口中的方法

java8-新特性-接口中的方法

前言 : JDK1.8 之前的回顾

  • 在jdk1.8之前,我们对接口的认知是这样的:

    1、方法:只能包含public和abstract的方法,即使定义为:

1
2
3
4
5
6
interface Shape {
//获取几何图形的面积
Double getArea(double length, double width);
}

方法前面也默认加了public abstract修饰

2、字段:只能包含常量,即public static final 修饰的变量

1
2
3
4
5
interface Shape {
int length = 0;
}

即使这样写,也是默认加上了public static final修饰。

问题展现

  • 现在我们有很多类实现了该接口,有三角形trangle,有圆形circle······
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Trangle implements Shape {

@Override
public Double getArea(double length, double width) {
return length * width / 2;
}
}

class Circle implements Shape {

@Override
public Double getArea(double length, double width) {
//调用方法时默认length传半径
return 3.14 * length * length;
}
}
  • 有一天,我们发现接口功能不够用了,需要增加一个计算周长的方法。

  • 这时候,JDK1.7及其之前版本该怎么办呢?

    • 管他三七二十一,往接口里直接加个方法,强迫所有实现该几何图形的接口的实现类(正方形、圆形、三角形······)都实现最新的接口方法
    • 把接口代码移到抽象类,添加一个有默认实现的计算周长的方法,但是所有实现类都要改为继承,遇到实现类继承别的父类就行不通了(java只有单继承)
    • 添加一个新接口,新接口里加上一个计算周长的方法,让所有实现了Shape接口的类再实现新的接口,看起来很美好,接口本身也是这么使用的,但是如果这个方法让所有的实现类再实现一遍,还是挺麻烦的。
  • JDK1.8针对这种接口不易扩展的现象,在接口新增default方法,可以有效解决上述遇到的接口拓展新方法的问题。原先的实现类不用改任何代码就拥有了新的能力,有点像从接口继承了一个有实现的方法,可以直接调用。
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class DefaultMethodTest {
public static void main(String[] args) {
System.out.println(new Circle().getPerimeterOfCircle(3));
}
}

interface Shape {
int a = 0;

public Double getArea(double length, double width);

/**
* 新增默认方法,为四边形扩展计算周长
*
* @param length 长
* @param width 款
* @return java.lang.Double
*/
default Double getPerimeterOfQuadrilateral(double length, double width) {
return (length + width) * 2;
}

/**
* 新增默认方法,为圆形计算周长
*
* @param redius 半径
* @return java.lang.Double
*/
default Double getPerimeterOfCircle(double redius) {
return 3.14 * 2 * redius;
}

}

class Trangle implements Shape {

@Override
public Double getArea(double length, double width) {
return length * width / 2;
}
}

class Circle implements Shape {

@Override
public Double getArea(double length, double width) {
//调用方法时默认length传半径
return 3.14 * length * length;
}
}

1. default 方法

  • java8以后,接口中可以添加使用default或者static修饰的方法,在这里我们只讨论default方法
  • default修饰方法只能在接口中使用
  • 在接口中被default标记的方法为普通方法,可以直接写方法体。

1.1 实现类会继承接口中的default方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
如果接口A中有default方法:
public interface A {
public default void a(){
System.out.println("这是A");
}
}

Test类实现接口A:

public class Test implements A{

}

那么Test类将会继承接口A中的a方法:

public class Test2 {
public static void main(String[] args) {
Test t = new Test();
t.a();
}
}

1.2 同时实现接口A和B,接口A和B中有相同的default方法

  • 如果一个类同时实现接口A和B,接口A和B中有相同的default方法,这时,该类必须重写接口中的default方法

为什么要重写呢?是因为,类在继承接口中的default方法时,不知道应该继承哪一个接口中的default方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
接口A:

public interface A {
public default void a(){
System.out.println("这是A");
}
}
接口B:

public interface B {
public default void a(){
System.out.println("这是B");
}
}

Test类:

1.3 同时继承父类和实现接口方法

  • 如果子类继承父类,父类中有b方法,该子类同时实现的接口中也有b方法(被default修饰),那么子类会继承父类的b方法而不是继承接口中的b方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
接口A:

public interface A {
public default void b(){
System.out.println("AAA");
}
}

类C:

public class C {
public void b(){
System.out.println("CCC");
}
}

子类:

public class Test extends C implements A{

}

测试类:

  • 说明子类继承的b方法为父类C中的b方法,不是接口中的default b(){}方法。

2. static 方法

  • 静态方法比较直观,类比普通类的静态方法
  • 就是可以不实例化,直接用类名调用的方法
  • 接口的静态方法也是一样,直接用接口名调用方法。
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class DefaultMethodTest {
public static void main(String[] args) {
System.out.println(new Circle().getPerimeterOfCircle(3));
System.out.println("-------------------------------------------");
System.out.println(Shape.describe());
}
}

interface Shape {
int a = 0;

public Double getArea(double length, double width);

/**
* 新增默认方法,为四边形扩展计算周长
*
* @param length 长
* @param width 款
* @return java.lang.Double
*/
default Double getPerimeterOfQuadrilateral(double length, double width) {
return (length + width) * 2;
}

/**
* 新增默认方法,为圆形计算周长
*
* @param redius 半径
* @return java.lang.Double
*/
default Double getPerimeterOfCircle(double redius) {
return 3.14 * 2 * redius;
}

/**
* 接口描述方法,描述接口用途及其他信息
*
* @return java.lang.String
*/
static String describe() {
return "我是一个几何图形接口";
}

}

class Trangle implements Shape {

@Override
public Double getArea(double length, double width) {
return length * width / 2;
}
}

class Circle implements Shape {

@Override
public Double getArea(double length, double width) {
//调用方法时默认length传半径
return 3.14 * length * length;
}
}

  • 可以惊奇地看到,用接口直接调了一个方法,这个现象可以类比匿名类。
  • 比如JDK1.7,我们要实现一个接口有一个方法,并且不用实例化的类来调用应该这么做:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class DefaultMethodTest {
public static void main(String[] args) {
System.out.println(new Shape() {
@Override
public Double getArea(double length, double width) {
return null;
}

@Override
public String describe() {
return "我是一个几何图形接口";
}
}.describe());
}
}

interface Shape {
int a = 0;

Double getArea(double length, double width);

String describe();

}

  • 但是这样做完全没有太大的意义,没有接口中定义静态方法来的优雅、简单。

3. 新的接口和抽象类对比

  • 带默认或者静态方法的接口像是一个抽象类吗?的确有点像

    • 但是抽象类可能包含可变状态(实例变量),而接口只能定义行为和常量。
  • 另外,一个类只能直接从一个类继承,但可以实现所需的任意数量的接口。

  • 所以,如果需要可变状态,并且确定某个类将构成一个合理的子类,则需要考虑一个抽象类。在其他情况下,使用具有默认/静态方法的接口就好了。

打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2019-2022 Zhuuu
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信