前面介绍了接口的基本用法,有心的朋友可能注意到这么一句话“在Java8之前,接口内部的全部方法都必须是抽象方法”,如此说来,在Java8以后,接口的内部方法也可能不是抽象方法了吗?之因此Java8对接口的定义规则发生变化,是由于原来的接口定义存在先天不足致使的,例以下列几点需求就难以知足:
一、Java8之前规定接口的内部方法只能是抽象方法,在该接口的实现类里面所有都要重写。这个规定明显太霸道了,为何非得全部都重写呢?有的行为分明是通用的,好比呼吸动做,凡是陆上动物都用鼻子呼吸,把新鲜空气吸进去,再把循环后的空气呼出来,这个呼吸方法理应放之四海而皆准,根本无需在每一个实现类中依次重写过去。
二、Java8之前的接口不支持构造方法也就算了,但是它竟然也不支持静态成员(包括静态属性和静态方法)!这下可苦了程序员,由于与行为有关的常量与工具方法不能放到接口内部,只能另外写个工具类填入这些常量与工具方法,因而本来应当在一个屋檐之下的行为动做和行为概念不得不分居两地了。
有鉴于此,从Java8开始,接口顺应时代要求进行了规则修订,针对以上的两点需求分别补充了相应的处理对策:
一、增长了默认方法,并经过前缀default来标识。接口内部须要编写默认方法的完整实现代码,这样实现类无需重写该方法便可直接继承并使用,仿佛默认方法就是父类方法同样,惟一的区别在于实现类不容许重写默认方法。
二、增长了静态属性和静态方法,并且都经过前缀static来标识。接口的静态属性同时也是终态属性,初始化赋值以后便没法再次修改;接口的静态方法不能被实现类继承,于是实现类容许定义同名的静态方法,缘于接口的静态方法与实现类的静态方法没有任何关联,仅仅是它俩刚好同名而已。
据此对先前的行为接口Behavior进行加强,按照Java8的新特性补充了默认方法与静态方法,修补以后的新接口ExpandBehavior代码以下所示:html
//定义一个增长了Java8新特性的接口 public interface ExpandBehavior { // 声明了一个抽象的飞翔方法 public void fly(); // 声明了一个抽象的游泳方法 public void swim(); // 声明了一个抽象的奔跑方法 public void run(); // 默认方法,之前缀default标识。默认方法不支持重写,但能够被继承。 public default String getOrigin(String place, String name, String ancestor) { return String.format("%s%s的祖先是%s。", place, name, ancestor); } public static int MALE = 0; public static int FEMALE = 1; // 接口内部的静态属性也默认为终态属性,因此final前缀可加可不加 //public final static int MALE = 0; //public final static int FEMALE = 1; // 静态方法,以关键字static标识。静态方法支持重写,但不能被继承。 public static String getNameByLeg(int leg_count) { if (leg_count == 2) { return "二足动物"; } else if (leg_count == 4) { return "四足动物"; } else if (leg_count >= 6) { return "多足动物"; } else { return "奇异动物"; } } }
根据上面的扩展接口,从新编写实现了该接口的鹅类,其中fly、swim、run这三个抽象方法均须重写,惟有默认方法getOrigin不要重写,而且鹅类代码当中能够直接调用这个默认方法。新写的鹅类代码ExpandGoose示例以下:java
//定义实现了扩展接口的鹅类 public class ExpandGoose extends Bird implements ExpandBehavior { public ExpandGoose(String name, int sexType) { super(name, sexType); } // 实现了接口的fly方法 @Override public void fly() { System.out.println("鹅飞不高,也飞不远。"); } // 实现了接口的swim方法 @Override public void swim() { System.out.println("鹅,鹅,鹅,曲项向天歌。白毛浮绿水,红掌拨清波。"); } // 实现了接口的run方法 @Override public void run() { System.out.println("槛外萧声轻荡漾,沙间鹅步满蹒跚。"); } // 根据产地和祖先拼接并打印该动物的描述文字 public void show(String place, String ancestor) { // getOrigin是来自扩展接口ExpandBehavior的默认方法,能够在实现类中直接使用 String desc = getOrigin(place, getName(), ancestor); System.out.println(desc); } }
接着轮到外部访问这个鹅类ExpandGoose了,表面上外部仍跟日常同样调用鹅类的成员方法,然而在调用接口的静态成员时有所差异。对于接口的静态属性,外部依然可以经过鹅类直接访问,访问格式形如“实现类的名称.静态属性名”;对于接口的静态方法,外部却不能经过鹅类访问了,由于实现类并未继承接口的静态方法,因此外部只能经过接口自身访问它的静态方法,访问格式形如“扩展接口的名称.静态方法名(***)”。下面是外部调用鹅类ExpandGoose的代码例子:程序员
// 演示扩展接口的实现类用法 private static void testExpand() { // 实现类能够继承接口的静态属性 ExpandGoose goose = new ExpandGoose("鹅", ExpandGoose.FEMALE); goose.show("中国", "鸿雁"); goose.show("欧洲", "灰雁"); // 接口中的静态方法没有被实现类所继承,于是只能经过扩展接口自身访问 String typeName = ExpandBehavior.getNameByLeg(2); System.out.println("鹅是"+typeName); }
运行上面的测试代码,观察到以下的日志结果,可见不论是默认方法getOrigin,仍是静态方法getNameByLeg,都获得了正确执行:ide
中国鹅的祖先是鸿雁。 欧洲鹅的祖先是灰雁。 鹅是二足动物
更多Java技术文章参见《Java开发笔记(序)章节目录》工具