Java开发笔记(五十九)Java8之后的扩展接口
前面介绍了接口的基本用法,有心的朋友可能注意到这么一句话“在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之后的扩展接口的更多相关文章
- Java开发笔记(十九)规律变化的for循环
前面介绍while循环时,有个名叫year的整型变量频繁出现,并且它是控制循环进出的关键要素.不管哪一种while写法,都存在三处与year有关的操作,分别是“year = 0”.“year<l ...
- Java开发笔记(九十九)定时器与定时任务
前面介绍了线程的几种运行方式,不管哪种方式,一旦调用了线程实例的start方法,都会立即启动线程的事务处理.然而某些业务场景在事务执行时间方面有特殊需求,例如期望延迟若干时间之后才开始事务运行,又如期 ...
- Java开发笔记(十五)短路逻辑运算的优势
前面提到逻辑运算只能操作布尔变量,这其实是不严谨的,因为经过Java编程实现,会发现“&”.“|”.“^”这几个逻辑符号竟然可以对数字进行运算.譬如下面的代码就直接对数字分别开展了“与”.“或 ...
- .Net开发笔记(十九) 创建一个可以可视化设计的对象
阅读本篇博客之前需要了解VS窗体设计器的工作原理,详细可参见本系列博客(十).(十一).(十二).必须需要知道的一条结论就是:处于窗体设计器(Form Designer)中的任何组件(包含控件,下同) ...
- Java学习笔记(十九)——Java 日志记录 AND log4j
[前面的话] 学习的进度应该稍微在快一点. Java日志到了必须学习怎么使用的时候了,因为在项目中要进行使用.基础性文章,选择性阅读. [结构] java日志对调试,记录运行,问题定位都起到了很重要的 ...
- JAVA学习第五十九课 — 网络编程概述
网络模型 OSI(Open System Interconnection)开放系统互连:參考模型 TCP/IP 网络通讯要素 IP地址 port号 传输协议 网络參考模型 七层OSI模型的基本概念要了 ...
- 【Java学习笔记之十九】super在Java继承中的用法小结
1)有人写了个很好的初始化属性的构造函数,而你仅仅想要在其中添加另一些自己新建属性的初始化,这样在一个构造函数中调用另外一个构造函数,可以避免重复的代码量,减少工作量: 2)在一个构造函数中调用另外一 ...
- Java开发笔记(十)一元运算符的技巧
前面讲到赋值运算符的时候,提到“x = x+7”可以被“x += 7”所取代,当然Java编程中给某个变量自加7并不常见,常见的是给某变量自加1,就像走台阶,一般都是一级一级台阶地走,犯不着一下子跳上 ...
- Java开发笔记(十二)布尔变量论道与或非
在编程语言的设计之初,它们除了可以进行数学计算,还常常用于逻辑推理和条件判断.为了实现逻辑判断的功能,Java引入了一种布尔类型boolean,用来表示“真”和“假”.该类型的变量只允许两个取值,即t ...
- Java开发笔记(十四)几种运算符的优先级顺序
到目前为止,我们已经学习了Java语言的好几种运算符,包括算术运算符.赋值运算符.逻辑运算符.关系运算符等基础运算符,并且在书写赋值语句时都没添加圆括号,显然是默认了先完成算术.逻辑.关系等运算,最后 ...
随机推荐
- scala 下 sigmoid 与breeze.numeric.sigmoid差异对比
scala> val beforeInit = System.nanoTime;val handsgn = rd.map(x => 1.0 / (1.0 + Math.exp(-x))); ...
- idea注册码
2019 idea 注册码: N757JE0KCT-eyJsaWNlbnNlSWQiOiJONzU3SkUwS0NUIiwibGljZW5zZWVOYW1lIjoid3UgYW5qdW4iLC ...
- 玩转vue前进刷新,后退不刷新and按需刷新
大白萝卜小课堂开讲了!带你玩转vue前进后退按需刷新! 用vue做后台管理项目,特别是有列表页.列表数据详情页.列表数据修改页功能的码友们,几乎都被vue前进后退都刷新的逻辑坑过,本萝卜更是! 萝卜的 ...
- FCC(ES6写法) Friendly Date Ranges
把常见的日期格式如:YYYY-MM-DD 转换成一种更易读的格式. 易读格式应该是用月份名称代替月份数字,用序数词代替数字来表示天 (1st 代替 1). 包含当前年份和相同月份的时候,makeFri ...
- 算法与数据结构(六) 迪杰斯特拉算法的最短路径(Swift版)
上篇博客我们详细的介绍了两种经典的最小生成树的算法,本篇博客我们就来详细的讲一下最短路径的经典算法----迪杰斯特拉算法.首先我们先聊一下什么是最短路径,这个还是比较好理解的.比如我要从北京到济南,而 ...
- 分享13道上海尚学堂拿回来的Java面试真题,这些都是Java核心常见问题,想拿OFFER必看!
上海尚学堂Java培训学员参加面试带回来的真题,分享出来与大家,希望大家能认真地看看做一遍.后面有详细题解答案,对照下,看看自己做得怎么样,把这些面试遇到的真题全部掌握,做好面试笔试前的准备. 一.1 ...
- Android OpenGL ES 开发(五): OpenGL ES 使用投影和相机视图
OpenGL ES环境允许你以更接近于你眼睛看到的物理对象的方式来显示你绘制的对象.物理查看的模拟是通过对你所绘制的对象的坐标进行数学变换完成的: Projection - 这个变换是基于他们所显示的 ...
- java web项目get,post请求参数中文乱码解决
[转载]原文地址:https://www.cnblogs.com/tom-plus/p/6392279.html 在开发过程中,有时候会碰到get,post请求参数中文乱码. 原因: Http请求传输 ...
- [Swift]LeetCode554. 砖墙 | Brick Wall
There is a brick wall in front of you. The wall is rectangular and has several rows of bricks. The b ...
- mybatis 详解------动态SQL
mybatis 详解------动态SQL 目录 1.动态SQL:if 语句 2.动态SQL:if+where 语句 3.动态SQL:if+set 语句 4.动态SQL:choose(when,o ...