Java8新特性之默认方法和静态方法
作者:编码是个技术活 发布时间:2021-08-29 13:34:26
前言
在Java 8之前,默认情况下,接口中的所有方法都是公共的和抽象的。但是这一限制在Java 8中被打破了,Java 8允许开发人员在接口中添加新方法,而无需在实现这些接口的类中进行任何更改。
为什么会有默认方法?
主要是为了方便扩展已有接口;如果没有默认方法,假如给Java中的某个接口添加一个新的抽象方法,那么所有实现了该接口的类都得修改,影响将非常大。
举个例子,Sortable <T>接口以及实现该接口的类SortableNumberCollection和SortableStringCollection。该接口有两种方法:void sort(); 和T peek()。
public interface Sortable<T> {
void sort();
T peek();
}
sort()方法用于对象排序,T peek()用于获取指定元素,另外需要一个比较器类ObjectComparator来对对象进行排序。
public class ObjectComparator implements Comparator<Comparable> {
@Override
public int compare(Comparable o1, Comparable o2) {
return o1.compareTo(o2);
}
}
SortableStringCollection是一个自定义集合类可以进行排序,并查看字符串指定元素,代码如下:
public class SortableStringCollection implements Sortable<String> {
private List<String> items = new ArrayList<>();
public void add(String item) {
items.add(item);
}
@Override
public void sort() {
items.sort(new ObjectComparator());
}
@Override
public String peek() {
return items.get(0);
}
}
同样,SortableNumberCollection是一个自定义集合类,其中包含可以使用接口方法进行排序和查看的数字列表指定元素,代码如下:
public class SortableNumberCollection implements Sortable<Integer> {
private List<Integer> items = new ArrayList<>();
public void add(Integer item) {
items.add(item);
}
@Override
public void sort() {
items.sort(new ObjectComparator());
}
@Override
public Integer peek() {
return items.get(0);
}
}
在Java8之前如果对接口Sortable<T>添加新方法:T sortAndPeek(),那么SortableStringCollection和
SortableNumberCollection都必须实现T sortAndPeek()方法。
Java8之后提供了一种新的实现方式,默认方法 default method,我们可以对Sortable<T>进行如下改造:
public interface Sortable<T> {
void sort();
T peek();
default T sortAndPeek(){ // New 'default method' added in the interface
sort();
return peek();
}
}
同时SortableStringCollection和SortableNumberCollection类不需要任何更改。这样可以减少我们对原有代码的改动。同时如果需要,还可以在实现此接口的任何类中重写该方法T sortAndPeek()的默认实现。
在下图中我们看到default Method不通的标识:
在多继承中使用默认方法问题
如果两个或多个接口具有相同的默认方法签名,并且一个类实现了这两个接口,则将引发编译时错误。例如:
public interface Interface1 {
void methodOne(String str);
default void newMethod(){
System.out.println("Interface1: Newly added method");
}
}
public interface Interface2 {
void methodTwo(String str);
default void newMethod(){
System.out.println("Interface2: Newly added method");
}
}
public class InterfaceImplementation implements Interface1, Interface2{
@Override
public void methodOne(String str) {
System.out.println("Overridden methodOne: " + str);
}
@Override
public void methodTwo(String str) {
System.out.println("Overridden methodTwo: " + str );
}
}
此时代码会提示如下异常:
InterfaceImplementation inherits unrelated defaults for newMethod() from types Interface1 and Interface2
要解决此问题,我们将必须重写类InterfaceImplementation中的方法:
public class InterfaceImplementation implements Interface1, Interface2{
@Override
public void methodOne(String str) {
System.out.println("Overridden methodOne: " + str);
}
// newMethod implemented to resolve the conflict.
@Override
public void newMethod() {
System.out.println("InterfaceImplementation: Newly added method");
}
@Override
public void methodTwo(String str) {
System.out.println("Overridden methodTwo: " + str );
}
}
我们总结一下:
类中的方法优先级最高。类或父类中声明的方法的优先级高于任何声明为默认方法的优先级。
如果无法依据第一条进行判断,那么子接口的优先级更高:函数签名相同时,优先选择拥有最具体实现的默认方法的接口,即如果B继承了A,那么B就比A更加具体。
最后,如果还是无法判断,继承了多个接口的类必须通过显式覆盖和调用期望的方法,显式地选择使用哪一个默认方法的实现。
在Java 8中添加静态方法
接口定义的静态方法独立于任何对象调用。所以,在调用静态方法时,不需要实现接口,也不需要接口的实例,
就像“默认方法”一样,“静态方法”也可以添加到接口中。例如,我们可以添加一个静态方法Direction getDefaultDirection(),该方法将返回默认Direction,例如:
public interface Sortable<T> {
Direction defaultDirection = Direction.DESC;
enum Direction {
ASC,
DESC
};
void sort();
T peek();
static Direction getDefaultDirection(){ // 'static method' added to the interface.
return defaultDirection;
}
}
在上面的示例中,可以使用类引用来调用静态Direction getDefaultDirection()方法:
Sortable.getDefaultDirection()
对默认方法和静态方法的一点思考
接口是设计模式中一种开闭原则的体验,而java8赋予了接口新的特性,使得接口使用起来更加的得心应手了,这也有助于我们更加内聚自己的代码结构了。Java源码中也有很多场景使用到了默认方法,例如:Iterator接口,我们在开发中可以多使用一些新的特性从而提高开发效率及增加代码的健壮性。
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
总结
来源:https://www.toutiao.com/i6932637837846135300/
猜你喜欢
- 本文实例讲述了C#修改及重置电脑密码DirectoryEntry实现方法。分享给大家供大家参考。具体如下:C#修改电脑密码方法如下:///
- 1、需要引入依赖<dependency> &l
- java多态性多态分两种:(1) 编译时多态(设计时多态):方法重载。(2) 运行时多态:J
- 平时我们在开发过程中,代码出现bug时为了更好的在服务器日志中寻找问题根源,会在接口的首尾打印日志,看下参数和返回值是否有问题。但是手动的l
- 目录简介普通邮件导入javax.mail依赖Spring配置类邮件配置:mail.properties测试基于thymeleaf模版的邮件导
- java 算法之快速排序实现代码摘要: 常用算法之一的快速排序算法的java实现原理:选择一个基准元素,通常选择第一个元素或者最后一个元素,
- Maven是个很好用的管理工具,不经能够管理jar,还能实现打包。这里讲解Maven 本地打包,服务器打包,可以全部交给jenkins去完成
- 微服务开发中经常有这样的需求,公司自定义了通用的请求头,需要在微服务的调用链中转发,比如在请求头中加入了token,或者某个自定义的信息un
- 由于ajax本身实际上是通过XMLHttpRequest对象来进行数据的交互,而浏览器出于安全考虑,不允许js代码进行跨域操作,所以会警告&
- 详解Java虚拟机管理的内存运行时数据区域概述 Java虚拟机在执行Java程序的过程中会把它所管理的内
- 在查询时经常出现一对多”的关系,所有会出现嵌套对象的情况,Mybatis在resultMap提供了collection标
- rocketmq消费者注册监听有两种模式有序消费MessageListenerOrderly和并发消费MessageListenerConc
- spring profile 多环境配置管理现象 如果在开发时进行一些数据库测试,希望链接到一个测试的数据库,以避免对开发数据
- 一般而言,一个项目部署的由:拉取代码->构建->测试->打包->部署等过程组成,如果我们经常需要部署项目,特别是在微
- FFmpeg是一个开源免费跨平台的视频和音频流方案,属于自由软件,采用LGPL或GPL许可证(依据你选择的组件)。它提供了录制、转换以及流化
- 一、整合原理二、导包(41个)1.hibernate(1)hibernate/lib/required(2)hibernate/lib/jp
- 一、实现流程1.注册2.登录3.登录保持【状态续签】二、实现方法项目结构1.引入依赖<!-- spring-web --><
- 一、方法的定义1.方法体中最后返回值可以使用return, 如果使用了return, 那么方法体的返回值类型一定要指定2.如果方法体重没有r
- 本篇随笔主要介绍用Java实现简单的装饰器设计模式:先来看一下装饰器设计模式的类图:从图中可以看到,我们可以装饰Component接口的任何
- Java try()语句实现try-with-resources异常管理机制java7 新增特性,对于try语句块中使用到的资源,不再需要手