详解 Java 8 中的 default 关键字和 @FunctionalInterface 注解
虚拟扩展方法是指:在接口内部包含了一些默认的方法实现,从而使得接口在进行扩展的时候,不会破坏与接口相关的实现类代码。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引入 default 关键字。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。通常用在将一个接口对象作为参数的情况,好处是在调用的时候只需要实现一个方法就可以了。1 概述本文介绍如下内容Java 8
虚拟扩展方法是指:在接口内部包含了一些默认的方法实现,从而使得接口在进行扩展的时候,不会破坏与接口相关的实现类代码。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引入 default 关键字。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。通常用在将一个接口对象作为参数的情况,好处是在调用的时候只需要实现一个方法就可以了。
本文介绍如下内容 java 8 之前接口中全是抽象方法,好处是面向抽象而不是面向具体编程。 缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,比如 java8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在 JDK 里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。 所以引入 default 关键字。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。 @FunctionalInterface 注解在编译的时候如果发现接口中没有或者超过一个抽象方法就会报错:Multiple non-overriding abstract methods found in interface ...,具体如下 也即是说带有 @FunctionalInterface 注解的接口只能有一个抽象方法,表示是函数式编程接口。 通常用在将一个接口对象作为参数的情况,好处是在调用的时候只需要实现一个方法就可以了。 这里以 java.util.Collection 接口中的 removeIf 方法为例。removeIf 方法源码如下 其中的 Predicate 对象就是用 @FunctionalInterface 注解修饰的接口,核心源码如下 现在有一个需求:集合 c 中有一些字符串,给定一个字符串 a,如果 a 在集合 c 中存在,就将集合中的 a 删除,传统的实现如下 如果在 Java 8 中,通过 removeIf 方法实现如下 从上面对比可以发现,代码大大简化。 函数式编程的好处个人感觉在于将 业务部分 和 固定的编程套路隔离。 业务部分通常的情况如下: 固定的编程套路如下 这里通过一个场景来实验函数式编程的好处以及带来的编程风格转变。 场景:从文件中读取 json 字符串,然后打印出来 传统的方式如下 如果使用函数式编程思想,那么应该将 固定的编程套路 封装成一个方法,这里定义成如下的方式 其中业务逻辑部分如下 未来如果想在读取文件的同时进行数据匹配或者筛选就不需要修改 processFile 方法,只需要在 Consumer 接口的实现中增加功能。1 概述
2 default 关键字介绍
2.1 为什么要增加 default 关键字
2.2 default 关键字的使用
import java.math.BigDecimal;@FunctionalInterfacepublic interface Calculate<T extends BigDecimal> { T add(T t1, T t2); default BigDecimal average(T t1, T t2) { return add(t1, t2).divide(new BigDecimal(2), 0, BigDecimal.ROUND_HALF_UP); }}
import java.math.BigDecimal;public class CalculateSub implements Calculate<BigDecimal> { @Override public BigDecimal add(BigDecimal t1, BigDecimal t2) { return t1.add(t2); } public static void main(String[] args) { CalculateSub calculateSub = new CalculateSub(); BigDecimal result = calculateSub.add(BigDecimal.valueOf(2), BigDecimal.valueOf(2)); System.out.println(String.format("result:%s", result)); // result:4 System.out.println(String.format("average:%s", calculateSub.average(BigDecimal.valueOf(4), BigDecimal.valueOf(2)))); // average:3 }}
3 @FunctionalInterface 注解
3.1 @FunctionalInterface 注解的特点
3.2 @FunctionalInterface 注解的使用
default boolean removeIf(Predicate<? super E> filter) { Objects.requireNonNull(filter); boolean removed = false; final Iterator<E> each = iterator(); while (each.hasNext()) { if (filter.test(each.next())) { each.remove(); removed = true; } } return removed;}
@FunctionalInterfacepublic interface Predicate<T> { boolean test(T t);}
List<String> dataList = new ArrayList<>();@Beforepublic void init() { dataList.add("a"); dataList.add("b"); dataList.add("c");}@Testpublic void test_7() { // 遍历集合,如果发现集合中存在就删除 String str = "a"; for (int i = 0; i < dataList.size(); i++) { if (dataList.get(i).equals(str)) { dataList.remove(i); } } // 打印 for (int i = 0; i < dataList.size(); i++) { System.out.println(dataList.get(i)); }}
@Testpublic void test_8() { // 遍历集合,如果发现集合中存在就删除 String str = "a"; dataList.removeIf(data -> data.equals(str)); // 打印 dataList.forEach(System.out::println);}
3.3 @FunctionalInterface 函数式编程的好处
@Testpublic void test_read_file() { try { InputStream inputStream = new FileInputStream(new File("D:\\git-workspace\\play\\test-java8-stream\\src\\test\\resources\\test.text")); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String temp = null; StringBuilder data = new StringBuilder(); while ((temp = bufferedReader.readLine()) != null) { data.append(temp); } JSONObject jsonObject = JSONObject.parseObject(data.toString()); System.out.println(JSONObject.toJSONString(jsonObject, true)); } catch (Exception e) { e.printStackTrace(); }}
import java.io.*;import java.util.function.Consumer;public class ReadFile { public static void processFile(File file, Consumer<String> consumer) { try { InputStream inputStream = new FileInputStream(file); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String temp; while ((temp = bufferedReader.readLine()) != null) { consumer.accept(temp); } } catch (Exception e) { e.printStackTrace(); } }}
@Testpublic void test_functional() { File file = new File("D:\\git-workspace\\play\\test-java8-stream\\src\\test\\resources\\test.text"); StringBuilder fileData = new StringBuilder(); ReadFile.processFile(file, data -> fileData.append(data)); JSONObject jsonObject = JSONObject.parseObject(fileData.toString()); System.out.println(JSONObject.toJSONString(jsonObject, true));}