Java8新特性系列-默認(rèn)方法和靜態(tài)方法
在Java 8之前,默認(rèn)情況下,接口中的所有方法都是公共的和抽象的。但是這一限制在Java 8中被打破了,Java 8允許開(kāi)發(fā)人員在接口中添加新方法,而無(wú)需在實(shí)現(xiàn)這些接口的類(lèi)中進(jìn)行任何更改。
為什么會(huì)有默認(rèn)方法?
主要是為了方便擴(kuò)展已有接口;如果沒(méi)有默認(rèn)方法,假如給Java中的某個(gè)接口添加一個(gè)新的抽象方法,那么所有實(shí)現(xiàn)了該接口的類(lèi)都得修改,影響將非常大。
舉個(gè)例子,Sortable
- public interface Sortable<T> {
- void sort();
- T peek();
- }
sort()方法用于對(duì)象排序,T peek()用于獲取指定元素,另外需要一個(gè)比較器類(lèi)ObjectComparator來(lái)對(duì)對(duì)象進(jìn)行排序。
- public class ObjectComparator implements Comparator<Comparable> {
- @Override
- public int compare(Comparable o1, Comparable o2) {
- return o1.compareTo(o2);
- }
- }
SortableStringCollection是一個(gè)自定義集合類(lèi)可以進(jìn)行排序,并查看字符串指定元素,代碼如下:
- public class SortableStringCollection implements Sortable<String> {
- private List<String> items = new ArrayList<>();
- public void add(String item) {
- items.add(item);
- }
- @Override
- public void sort() {
- items.sort(new ObjectComparator());
- }
- @Override
- public String peek() {
- return items.get(0);
- }
- }
同樣,SortableNumberCollection是一個(gè)自定義集合類(lèi),其中包含可以使用接口方法進(jìn)行排序和查看的數(shù)字列表指定元素,代碼如下:
- public class SortableNumberCollection implements Sortable<Integer> {
- private List<Integer> items = new ArrayList<>();
- public void add(Integer item) {
- items.add(item);
- }
- @Override
- public void sort() {
- items.sort(new ObjectComparator());
- }
- @Override
- public Integer peek() {
- return items.get(0);
- }
- }
在Java8之前如果對(duì)接口Sortable
SortableNumberCollection都必須實(shí)現(xiàn)T sortAndPeek()方法。
Java8之后提供了一種新的實(shí)現(xiàn)方式,默認(rèn)方法 default method,我們可以對(duì)Sortable
- public interface Sortable<T> {
- void sort();
- T peek();
- default T sortAndPeek(){ // New 'default method' added in the interface
- sort();
- return peek();
- }
- }
同時(shí)SortableStringCollection和SortableNumberCollection類(lèi)不需要任何更改。這樣可以減少我們對(duì)原有代碼的改動(dòng)。同時(shí)如果需要,還可以在實(shí)現(xiàn)此接口的任何類(lèi)中重寫(xiě)該方法T sortAndPeek()的默認(rèn)實(shí)現(xiàn)。
在下圖中我們看到default Method不通的標(biāo)識(shí):

在多繼承中使用默認(rèn)方法問(wèn)題
如果兩個(gè)或多個(gè)接口具有相同的默認(rèn)方法簽名,并且一個(gè)類(lèi)實(shí)現(xiàn)了這兩個(gè)接口,則將引發(fā)編譯時(shí)錯(cuò)誤。例如:
- public interface Interface1 {
- void methodOne(String str);
- default void newMethod(){
- System.out.println("Interface1: Newly added method");
- }
- }
- public interface Interface2 {
- void methodTwo(String str);
- default void newMethod(){
- System.out.println("Interface2: Newly added method");
- }
- }
- public class InterfaceImplementation implements Interface1, Interface2{
- @Override
- public void methodOne(String str) {
- System.out.println("Overridden methodOne: " + str);
- }
- @Override
- public void methodTwo(String str) {
- System.out.println("Overridden methodTwo: " + str );
- }
- }
此時(shí)代碼會(huì)提示如下異常:
- InterfaceImplementation inherits unrelated defaults for newMethod() from types Interface1 and Interface2
要解決此問(wèn)題,我們將必須重寫(xiě)類(lèi)InterfaceImplementation中的方法:
- public class InterfaceImplementation implements Interface1, Interface2{
- @Override
- public void methodOne(String str) {
- System.out.println("Overridden methodOne: " + str);
- }
- // newMethod implemented to resolve the conflict.
- @Override
- public void newMethod() {
- System.out.println("InterfaceImplementation: Newly added method");
- }
- @Override
- public void methodTwo(String str) {
- System.out.println("Overridden methodTwo: " + str );
- }
- }
我們總結(jié)一下:
- 類(lèi)中的方法優(yōu)先級(jí)最高。類(lèi)或父類(lèi)中聲明的方法的優(yōu)先級(jí)高于任何聲明為默認(rèn)方法的優(yōu)先級(jí)。
- 如果無(wú)法依據(jù)第一條進(jìn)行判斷,那么子接口的優(yōu)先級(jí)更高:函數(shù)簽名相同時(shí),優(yōu)先選擇擁有最具體實(shí)現(xiàn)的默認(rèn)方法的接口,即如果B繼承了A,那么B就比A更加具體。
- 最后,如果還是無(wú)法判斷,繼承了多個(gè)接口的類(lèi)必須通過(guò)顯式覆蓋和調(diào)用期望的方法,顯式地選擇使用哪一個(gè)默認(rèn)方法的實(shí)現(xiàn)。
在Java 8中添加靜態(tài)方法
接口定義的靜態(tài)方法獨(dú)立于任何對(duì)象調(diào)用。所以,在調(diào)用靜態(tài)方法時(shí),不需要實(shí)現(xiàn)接口,也不需要接口的實(shí)例,
就像“默認(rèn)方法”一樣,“靜態(tài)方法”也可以添加到接口中。例如,我們可以添加一個(gè)靜態(tài)方法Direction getDefaultDirection(),該方法將返回默認(rèn)Direction,例如:
- public interface Sortable<T> {
- Direction defaultDirection = Direction.DESC;
- enum Direction {
- ASC,
- DESC
- };
- void sort();
- T peek();
- static Direction getDefaultDirection(){ // 'static method' added to the interface.
- return defaultDirection;
- }
- }
在上面的示例中,可以使用類(lèi)引用來(lái)調(diào)用靜態(tài)Direction getDefaultDirection()方法:
- Sortable.getDefaultDirection()
對(duì)默認(rèn)方法和靜態(tài)方法的一點(diǎn)思考
接口是設(shè)計(jì)模式中一種開(kāi)閉原則的體驗(yàn),而java8賦予了接口新的特性,使得接口使用起來(lái)更加的得心應(yīng)手了,這也有助于我們更加內(nèi)聚自己的代碼結(jié)構(gòu)了。Java源碼中也有很多場(chǎng)景使用到了默認(rèn)方法,例如:Iterator接口,我們?cè)陂_(kāi)發(fā)中可以多使用一些新的特性從而提高開(kāi)發(fā)效率及增加代碼的健壯性。
- public interface Iterable<T> {
- Iterator<T> iterator();
- default void forEach(Consumer<? super T> action) {
- Objects.requireNonNull(action);
- for (T t : this) {
- action.accept(t);
- }
- }
- default Spliterator<T> spliterator() {
- return Spliterators.spliteratorUnknownSize(iterator(), 0);
- }
- }
【編輯推薦】