您的位置 首页 java

Java8 – 新特性之接口的默认方法和静态方法

一、Java8中接口的新功能

在Java8之前的版本,我们在接口中只能定义静态变量和抽象方法(只有方法定义,没有方法体)。

例如:

 public interface I1{
        // 静态变量
        public static String NAME = "I1";
  
        // 抽象方法
        void sayHello();
 }  

在Java8,对接口增加了新的功能,静态方法和默认方法。

二、接口的默认方法

1.为什么要有这个特性?

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

2.默认方法定义

默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。

默认方法的定义很简单,我们只需在接口的方法名前面加个 default 关键字即可实现默认方法。

例如:

 public interface I1 {
        default void hello() {
            System.out.println("Hello from I1");
        }
 }

public class C1 implements I1 {
       @ Override 
        public void hello() {
             System.out.println("Hello from C1");
        }
}     

3.默认方法的特征

1)默认方法可以调用接口中的抽象方法

例如:

 public interface I1 {
        default void hello() {
            System.out.println("Hello from I1");
        }
 }

 public class C1 implements I1 {
       @Override
        public void hello() {
             System.out.println("Hello from C1");
        }
}     

2)默认方法属于实例,子类可以根据需要覆盖默认方法

例如:

 public interface I1 {
        default void hello() {
            System.out.println("Hello from I1");
        }
 }

 public class C1 implements I1 {
       @Override
        public void hello() {
             System.out.println("Hello from C1");
        }
}     

4.多个同名默认方法

在Java中,一个类可以实现多个接口,如果被实现的多个接口中存在相同签名的默认方法,类会根据下面三条规则来进行选择:

1)类中的方法优先级最高,类或父类中声明的方法的优先级高于任何声明为默认方法的优先级。

2)如果第一条无法判断,那么子接口的优先级更高:方法签名相同时,优先选择拥有最具体实现的默认方法的接口, 即如果B继承了A,那么B就比A更加具体。

3)最后,如果还是无法判断,继承了多个接口的类必须通过显式覆盖和调用期望的方法, 显式地选择使用哪一个默认方法的实现。

例子1:

类C1中没有声明方法hello, I2继承I1,依据规则2,C1的实例将会调用接口I2的默认方法。

 public class Example1 {
    public static void main(String[] args) {
        // 输出 Hello from I2
        new C1().hello();
    }

    public interface I1 {
        default void hello() {
            System.out.println("Hello from I1");
        }
    }

    public interface I2 extends I1 {
        default void hello() {
            System.out.println("Hello from I2");
        }
    }

    public static class C1 implements I1, I2 {
    }
}  

例子2:

C2,C1都没有声明hello方法,I2继承I1,基于规则2,C2的实例会调用I2接口的默认方法。

 public class Example2 {
    public static void main(String[] args) {
        //输出Hello from I2
        new C2().hello();
    }

    public interface I1 {
        default void hello() {
            System.out.println("Hello from I1");
        }
    }

    public interface I2 extends I1 {
        default void hello() {
            System.out.println("Hello from I2");
        }
    }

    public static class C1 implements I1{

    }
    public static class C2 extends C1 implements I1, I2 {
    }
}  

例子3:

C2继承了C1,但没有声明hello方法,C1覆盖了hello方法,根据规则1,C2的实例将会调用C1中的hello方法。

 public class Example3 {
    public static void main(String[] args) {
        //输出 Hello from C1
        new C2().hello();
    }

    public interface I1 {
        default void hello() {
            System.out.println("Hello from I1");
        }
    }

    public interface I2 extends I1 {
        default void hello() {
            System.out.println("Hello from I2");
        }
    }

    public static class C1 implements I1{
        @Override
        public void hello() {
            System.out.println("Hello from C1");
        }
    }
    public static class C2 extends C1 implements I1, I2 {
    }
}  

例子4:

C1同时实现了I1和I2, 由于I1和I2存在同样签名的hello方法,根据规则3,C1必须覆盖hello方法,可以在方法内显式选择调用I1或者I2的方法。

 public class Example4 {
    public static void main(String[] args) {
        // 输出 Hello from I1
        new C1().hello();
    }

    public interface I1 {
        default void hello() {
            System.out.println("Hello from I1");
        }
    }

    public interface I2 {
        default void hello() {
            System.out.println("Hello from I2");
        }
    }

    /**
     * 由于编译器无法识别I1还是I2的实现更加具体,所以会抛出编译错误:”类 C1从类型 I1 和 I2 中继承了hello() 的不相关默认值“
     * 要解决冲突,可以在C1中覆盖hello()方法并在方法内显式的选择调用I1还是I2的方法
     */    public static class C1 implements I1, I2 {
        /**
         * 显式地选择调用接口I1和 I2中的hello方法
         */        @Override
        public void hello() {
            I1.super.hello();
            //I2.super.hello();
        }
    }
}  

5.默认方法和抽象类的区别

1)一个类只能继承一个抽象类;但一个类可以实现多个带有默认方法接口。

2)抽象类有实例变量,而接口只能有类变量(即静态常量)。

三、接口的静态方法

Java 8 的另一个特性是接口可以声明静态方法。比如:

 public interface I1{
        // 静态变量
        public static String NAME = "I1";
// 静态方法
        static String getName(){
            return NAME;
        }
 }  

四、默认方法和静态方法的区别

1)默认方法属于类实例,静态方法属于类(接口);

2)默认方法可以被继承,静态方法不会被继承(但接口中的静态变量可以被继承);

3)默认方法可以访问接口的抽象方法,静态方法则不可以。

例如:

 public class Example1 {
    public static void main(String[] args) {
        System.out.println("I1.NAME:" + I1.NAME);
        System.out.println("I1.getName():" + I1.getName());
        // 接口中的静态变量可以被子类继承
        System.out.println("C1.Name:" + C1.NAME);
        // 这里会抛出错误,提示静态方法无法给子类继承 'Static method may be invoked on containing interface class only'
        //System.out.println("C1.getName():" + C1.getName());
        // 默认方法可以被子类继承
        new C1().hello();
    }

    public interface I1{
        // 静态变量
        public static String NAME = "I1";
        // 默认方法
        default void hello(){
            System.out.println("Hello from I1");
            //允许调用接口的抽象方法
            sayHello();
        }
        // 抽象方法
        void sayHello();

        // 静态方法
        static String getName(){
            return NAME;
        }
    }

    public static class C1 implements I1{
        @Override
        public void sayHello() {
            System.out.println("I am C1.");
        }
    }
}  

五、参考资料

Java 8 默认方法

一文带你认识Java8中接口的默认方法

文章来源:智云一二三科技

文章标题:Java8 – 新特性之接口的默认方法和静态方法

文章地址:https://www.zhihuclub.com/174787.shtml

关于作者: 智云科技

热门文章

网站地图