目录

前言

1、什么是默认方法和静态默认方法

2、为什么接口需要默认方法?

3、默认方法冲突问题

4、总结


前言

面试官:接口里面能有实现方法吗?
库里:接口不可以有,但是抽象类有。
面试官:确定嘛?那问个设计性的问题:假如有一个接口,有10个实现类,现在的需求可能要往接口加一个方法,这样就要改动10个实现类,但需求只需要改动其中两个实现类,要怎么设计呢?
库里:emm…

针对上面的问题,有2个方案:

  • 再定义一个抽象类去实现这个接口,在抽象类中新增这个方法,然后其他两个实现类实现这个抽象类就好了。这种方案有个缺点:因为类是单继承接口多实现的特点,假如这2个类已经继承了其他的类,这个方案将行不通。
  • 使用Java8中的新特性,在接口中新增默认方法或者静态默认方法,这也是本文的重点。

 

1、什么是默认方法和静态默认方法

在Java8中接口可以有默认方法和默认静态方法(就是我们经常使用的静态方法)。
默认方法用default修饰,一个接口中可以有多个默认方法。
静态方法用static修饰,这个你肯定经常使用了,我们用 类.方法名() 的方式来访问。同样一个接口中可以有多个静态方法。

我们来看下在 JDK API 中 java.lang.Iterable接口的定义 和 java.util.Map 中的内部接口Entry关于接口默认方法和静态方法的应用:

// 定义了多个默认方法
public interface Iterable<T> {
    /**
     * @since 1.8 --- 可以看到这个特性是从1.8开始才有的。
     */
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

    /**
     * @since 1.8
     */
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

// 定义了多个静态默认方法。
interface Entry<K, V> {
    public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K, V>> comparingByKey() {
        return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> c1.getKey().compareTo(c2.getKey());
    }

    public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K, V>> comparingByValue() {
        return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> c1.getValue().compareTo(c2.getValue());
    }
}

 

2、为什么接口需要默认方法?

首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的 java 8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题

针对上面的面试题,就很解决啦!!!

 

3、默认方法冲突问题

一个接口有默认方法,考虑这样的情况,一个类实现了多个接口,且这些接口有相同的默认方法,以下实例说明了这种情况的解决方法:

public interface Vehicle {
   default void print(){
      System.out.println("我是一辆车!");
   }
}
 
public interface FourWheeler {
   default void print(){
      System.out.println("我是一辆四轮车!");
   }
}

第一个解决方案是创建自己的默认方法,来覆盖重写接口的默认方法:

public class Car implements Vehicle, FourWheeler {
   default void print(){
      System.out.println("我是一辆四轮汽车!");
   }
}

第二种解决方案可以使用 super 来调用指定接口的默认方法:

public class Car implements Vehicle, FourWheeler {
   public void print(){
      Vehicle.super.print();
   }
}

4、总结

以后面试官问你同样的问题,你知道怎么回答了吧!