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接口靜態/默認方法
帶默認或者靜態方法的接口像是一個抽象類嗎?的確有點像,但是抽象類可能包含可變狀態(實例變量),而接口只能定義行爲和常量。另外,一個類只能直接從一個類繼承,但可以實現所需的任意數量的接口。所以,如果需要可變狀態,並且確定某個類將構成一個合理的子類,則需要考慮一個抽象類。在其他情況下,使用具有默認/靜態方法的接口就好了。