java8学习笔记(3)-接口默认方法和静态方法

2019/06/14

简介

在 Java8 之前,在接口中只能有方法声明而不能有方法实现,实现类必须实现接口所声明的所有方法。Java8 允许我们在接口中为方法添加默认实现,并且允许我们为接口添加静态方法。

接口默认方法

default关键字的使用

@FunctionalInterface
public interface Example {
    void hello();

    default void hi() {
        System.out.println("hi : Example");
    }
}

在接口 Example 中,方法 void hi() 的前面有关键字 default,这个方法就是一个默认方法。

继承了 Example 接口的实现类必须实现 void hello() 方法,但可以不实现 void hi() 方法:

public class ExampleImpl implements Example {

    @Override
    public void hello() {
        System.out.println("hello : ExampleImpl");
    }

    public static void main(String[] args) {
        Example example = new ExampleImpl();
        example.hello();
        example.hi();
    }
}

运行 main 方法结果:

hello : ExampleImpl
hi : Example

实现类相当于继承了接口中的 void hi() 方法实现,和类间继承是一样的。实现类也可以覆写接口的默认方法:

public class ExampleImpl implements Example {

    @Override
    public void hello() {
        System.out.println("hello : ExampleImpl");
    }

    @Override
    public void hi() {
        System.out.println("hi : ExampleImpl");
    }

    public static void main(String[] args) {
        Example example = new ExampleImpl();
        example.hi();
    }
}

运行 main 方法结果:

hi : ExampleImpl

默认方法冲突

如果一个类同时实现两个接口,且两个不同接口中存在签名相同的默认方法,此时将出现冲突:

public interface Example1 {
    default void hi() {
        System.out.println("hi : Example1");
    }
}

public interface Example2 {
    default void hi() {
        System.out.println("hi : Example2");
    }
}

// 从Example1和Example2继承的默认方法存在冲突,无法通过编译
public class ExampleImpl implements Example1,Example2 {

}

此时,实现类必须覆写存在冲突的方法:

public class ExampleImpl implements Example1,Example2 {
    @Override
    public void hi() {
        System.out.println("hi : ExampleImpl");
    }
}

如果一个类从父类中继承到某个接口的默认方法,同时自己又实现了另一个接口,而接口中的默认方法与父类中的默认方法具有相同的签名,这种情况也不能通过编译:

public interface Example1 {
    default void hi() {
        System.out.println("hi : Example1");
    }
}

public interface Example2 {
    default void hi() {
        System.out.println("hi : Example2");
    }
}

public class SuperExampleImpl implements Example1 {

}

// 从Example1和Example2继承的默认方法存在冲突,无法通过编译
public class ExampleImpl extends SuperExampleImpl implements Example2 {

}

但是,如果父类覆写了默认方法,此时子类优先会采用父类的方法实现:

public interface Example1 {
    default void hi() {
        System.out.println("hi : Example1");
    }
}

public interface Example2 {
    default void hi() {
        System.out.println("hi : Example2");
    }
}

public class SuperExampleImpl implements Example1 {
    @Override
    public void hi() {
        System.out.println("hi : SuperExampleImpl");
    }
}

public class ExampleImpl extends SuperExampleImpl implements Example2 {
    public static void main(String[] args) {
        ExampleImpl example = new ExampleImpl();
        example.hi();
    }
}

main 方法运行结果:

hi : SuperExampleImpl

优先采用父类中的方法,可以更好地兼容之前的 JDK 版本。从旧版本迁移到 Java8 以上后,如果要在接口中添加默认方法,默认方法不会与实现类中已有的方法发生冲突。

在函数式接口的规约中,声明与 Object 类公有方法具有相同签名的方法,将不被算作抽象发放,或许也有优先级上的原因。

接口的静态方法

Java8 允许我们在接口中添加静态方法,接口中的静态方法不会被实现类继承。我们可以通过接口名来调用接口的静态方法。

public interface Example {
    default void hi() {
        System.out.println("hi : Example");
    }

    static void hello(){
        System.out.println("hello : Example");
    }
}

调用:

public class Main {
    public static void main(String[] args) {
        Example.hello();
    }
}

运行结果:

hello : Example

总结

Java8 对接口进行了增强,让接口的使用变得更加灵活。Java8 之后,接口和抽象类曾经的一个相异点转而变成了共同点(允许有方法实现),但这两者从设计理念上来说是不同的,滥用接口默认方法是有害的。


(转载本站文章请注明作者和出处)

Show Disqus Comments

Post Directory