前面介绍了接口的基本用法,有心的朋友可能注意到这么一句话“在Java8以前,接口内部的所有方法都必须是抽象方法”,如此说来,在Java8之后,接口的内部方法也可能不是抽象方法了吗?之所以Java8对接口的定义规则发生变化,是因为原来的接口定义存在先天不足导致的,例如下列几点需求就难以满足:
1、Java8以前规定接口的内部方法只能是抽象方法,在该接口的实现类里面全部都要重写。这个规定明显太霸道了,为什么非得所有都重写呢?有的行为分明是通用的,比如呼吸动作,凡是陆上动物都用鼻子呼吸,把新鲜空气吸进去,再把循环后的空气呼出来,这个呼吸方法理应放之四海而皆准,根本无需在每个实现类中依次重写过去。
2、Java8以前的接口不支持构造方法也就算了,可是它居然也不支持静态成员(包括静态属性和静态方法)!这下可苦了程序员,因为与行为有关的常量与工具方法不能放到接口内部,只能另外写个工具类填入这些常量与工具方法,于是原本应当在一个屋檐之下的行为动作和行为概念不得不分居两地了。
有鉴于此,从Java8开始,接口顺应时代要求进行了规则修订,针对以上的两点需求分别补充了相应的处理对策:
1、增加了默认方法,并通过前缀default来标识。接口内部需要编写默认方法的完整实现代码,这样实现类无需重写该方法即可直接继承并使用,仿佛默认方法就是父类方法一样,唯一的区别在于实现类不允许重写默认方法。
2、增加了静态属性和静态方法,而且都通过前缀static来标识。接口的静态属性同时也是终态属性,初始化赋值之后便无法再次修改;接口的静态方法不能被实现类继承,因而实现类允许定义同名的静态方法,缘于接口的静态方法与实现类的静态方法没有任何关联,仅仅是它俩恰好同名而已。
据此对先前的行为接口Behavior进行增强,按照Java8的新特性补充了默认方法与静态方法,修补之后的新接口ExpandBehavior代码如下所示:

//定义一个增加了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示例如下:

//定义实现了扩展接口的鹅类
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,都得到了正确执行:

中国鹅的祖先是鸿雁。
欧洲鹅的祖先是灰雁。
鹅是二足动物

  

更多Java技术文章参见《Java开发笔记(序)章节目录

Java开发笔记(五十九)Java8之后的扩展接口的更多相关文章

  1. Java开发笔记(十九)规律变化的for循环

    前面介绍while循环时,有个名叫year的整型变量频繁出现,并且它是控制循环进出的关键要素.不管哪一种while写法,都存在三处与year有关的操作,分别是“year = 0”.“year<l ...

  2. Java开发笔记(九十九)定时器与定时任务

    前面介绍了线程的几种运行方式,不管哪种方式,一旦调用了线程实例的start方法,都会立即启动线程的事务处理.然而某些业务场景在事务执行时间方面有特殊需求,例如期望延迟若干时间之后才开始事务运行,又如期 ...

  3. Java开发笔记(十五)短路逻辑运算的优势

    前面提到逻辑运算只能操作布尔变量,这其实是不严谨的,因为经过Java编程实现,会发现“&”.“|”.“^”这几个逻辑符号竟然可以对数字进行运算.譬如下面的代码就直接对数字分别开展了“与”.“或 ...

  4. .Net开发笔记(十九) 创建一个可以可视化设计的对象

    阅读本篇博客之前需要了解VS窗体设计器的工作原理,详细可参见本系列博客(十).(十一).(十二).必须需要知道的一条结论就是:处于窗体设计器(Form Designer)中的任何组件(包含控件,下同) ...

  5. Java学习笔记(十九)——Java 日志记录 AND log4j

    [前面的话] 学习的进度应该稍微在快一点. Java日志到了必须学习怎么使用的时候了,因为在项目中要进行使用.基础性文章,选择性阅读. [结构] java日志对调试,记录运行,问题定位都起到了很重要的 ...

  6. JAVA学习第五十九课 — 网络编程概述

    网络模型 OSI(Open System Interconnection)开放系统互连:參考模型 TCP/IP 网络通讯要素 IP地址 port号 传输协议 网络參考模型 七层OSI模型的基本概念要了 ...

  7. 【Java学习笔记之十九】super在Java继承中的用法小结

    1)有人写了个很好的初始化属性的构造函数,而你仅仅想要在其中添加另一些自己新建属性的初始化,这样在一个构造函数中调用另外一个构造函数,可以避免重复的代码量,减少工作量: 2)在一个构造函数中调用另外一 ...

  8. Java开发笔记(十)一元运算符的技巧

    前面讲到赋值运算符的时候,提到“x = x+7”可以被“x += 7”所取代,当然Java编程中给某个变量自加7并不常见,常见的是给某变量自加1,就像走台阶,一般都是一级一级台阶地走,犯不着一下子跳上 ...

  9. Java开发笔记(十二)布尔变量论道与或非

    在编程语言的设计之初,它们除了可以进行数学计算,还常常用于逻辑推理和条件判断.为了实现逻辑判断的功能,Java引入了一种布尔类型boolean,用来表示“真”和“假”.该类型的变量只允许两个取值,即t ...

  10. Java开发笔记(十四)几种运算符的优先级顺序

    到目前为止,我们已经学习了Java语言的好几种运算符,包括算术运算符.赋值运算符.逻辑运算符.关系运算符等基础运算符,并且在书写赋值语句时都没添加圆括号,显然是默认了先完成算术.逻辑.关系等运算,最后 ...

随机推荐

  1. 购物车自己sql错误

    $user_id=$_GET['user_id']; if(!$user_id){ $arr=array('code'=>-1,'data'=>"用户不存在"); ec ...

  2. ArcMap AddIn之下载ArcGIS Server地图服务中的数据

    涉及到开发知识点1.ArcGIS Server地图服务 2.C# web请求获取数据 3.AddIN开发技术 工具界面: 具体涉及到的代码之后有空贴出来.先上工具 AddIn插件下载地址:点击这里下载 ...

  3. db2数据库常见问题

    db2数据库不能轻易改变表结构,不然表会进入暂挂状态,造成表被锁住. 解锁表语句:call sysproc.admin_cmd('reorg table <table name>');

  4. Raiden Charge

    2017年10月22 周日 这是个元气满满 值得纪念的一天(不好意思走错片场了) 虽然有各种乱遭的客观元素 但我们队确确实实地打铁了 那些我们轻视的 野鸡(误)大学 都在我们前面 都说知耻而后勇 虽然 ...

  5. Egret 生成 自带EUI 的微信小游戏 踩坑!

    1. 首先,再次被网上一大堆屎一样的资料搞得浪费了我一天时间.各种坑. 2. 本文先讲一种正确的方式,然后再列举坑. 去www.egret.com下载最新的引擎,我的最新版本是5.2.2. 然后就会被 ...

  6. monkey 命令详解

    monkey命令详解   1.  $ adb shell monkey <event-count>                <event-count>是随机发送事件数 例 ...

  7. 利用Python实现对Web服务器的目录探测

    今天是一篇提升技能的干货分享,操作性较强,适用于中级水平的小伙伴,文章阅读用时约3分钟. PART 1/Python Python是一种解释型.面向对象.动态数据类型的高级程序设计语言. Python ...

  8. [Swift]LeetCode2. 两数相加 | Add Two Numbers

    You are given two non-empty linked lists representing two non-negative integers. The digits are stor ...

  9. 使用jquery日期选择器flatpickr.js,使用js动态创建input元素时插件失效

    最近写页面时需要用到,日期选择器,网上搜索了一些插件,最后使用了flatpickr.js.我是从npm 上拉下的依赖  npm install flatpickr --save 随后在页面中引入css ...

  10. 像素数据YUV简介与觉存储格式介绍

    主要学习链接:博客园.51CTO 前言 照例是先废话几句,下面的内容都是在学习时从网上找来的,并非我原创,我之所以要写这篇笔记是因为网的内容都很分散,找的时候要从各个地方看,很不方便,所以就自己总结了 ...