JDK1.8之前的接口回顾
在jdk1.8之前,我们对接口的认知是这样的:
1、方法:只能包含public和abstract的方法,即使定义为:
interface Shape {
//获取几何图形的面积
Double getArea(double length, double width);
}
方法前面也默认加了public abstract修饰
2、字段:只能包含常量,即public static final 修饰的变量
interface Shape {
int length = 0;
}
即使这样写,也是默认加上了public static final修饰。
遇到的问题
现在我们有很多类实现了该接口,有三角形trangle,有圆形circle······
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方法,可以有效解决上述遇到的接口拓展新方法的问题。原先的实现类不用改任何代码就拥有了新的能力,有点像从接口继承了一个有实现的方法,可以直接调用。
具体实现:
package com.changan.eurekademo.onepointeight;
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;
}
}
可以看到输出结果是:
静态方法
静态方法比较直观,类比普通类的静态方法,就是可以不实例化,直接用类名调用的方法,接口的静态方法也是一样,直接用接口名调用方法。
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,我们要实现一个接口有一个方法,并且不用实例化的类来调用应该这么做:
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();
}
但是这样做完全没有太大的意义,没有接口中定义静态方法来的优雅、简单。
抽象类VS接口静态/默认方法
带默认或者静态方法的接口像是一个抽象类吗?的确有点像,但是抽象类可能包含可变状态(实例变量),而接口只能定义行为和常量。另外,一个类只能直接从一个类继承,但可以实现所需的任意数量的接口。所以,如果需要可变状态,并且确定某个类将构成一个合理的子类,则需要考虑一个抽象类。在其他情况下,使用具有默认/静态方法的接口就好了。