Java 8方法引用使用指南
【编者按】本文作者为拥有15年 Java 开发经验的资深程序员 Per-Åke Minborg,主要介绍如何灵活地解析 Java 中的方法引用。文章系国内 ITOM 管理平台 OneAPM 编译呈现。
众所周知,在Java 8中我们可以使用方法引用。譬如,在我们需要遍历流元素时,可以使用 String::isEmpty 来引用isEmpty方法。试看下面这段代码:
Stream.of("A", "", "B").filter(Stream::isEmpty).count();
运行的结果为1(因为在这个流中只有一个空元素)。但是,如果我们要过滤出非空字符串,我们得写成.filter(s - !s.isEmpty())。这是一个Lambda表达式。显然,这儿有个讨厌的不对称想象。我们可以使用方法引用,但却不能用它的反式。我们可以写predicate.negate()却不能写Stream::isEmpty.negate()或!Stream::isEmpty。
为什么呢?这是因为方法引用并非Lambda表达式或者函数接口。不过,使用Java的类型推导可以将方法引用解析为一个或多个函数接口。上例中的String::isEmpty至少可以解析为:
Predicate String Function String, Boolean
所以,我们要排除其他可能性,确定到底将方法引用转换为哪个函数接口。本文在一定程度上解决了该问题。文中的代码来自开源项目Speedment,它让数据库看起来像Java 8的流。
解析方法引用其实,以静态方法为“管道”,可以部分地解决这个问题——该静态方法以一个方法引用为输入,以特定的函数接口为其返回。试考虑下面这个简短的静态方法:
public static T Predicate T as(Predicate T predicate) { return predicate;
现在,如果静态地导入这个方法,事实上,我们就能更简单地使用方法引用。如下例所示:
Stream.of("A", "", "B").filter(as(String::isEmpty).negate()).count();
这段代码返回的结果为2,即流中非空元素的数量。有关方法引用的使用方式,我们又向前迈进了一步。另一个好处是,这个解决方案让我们更轻松地编写predicates接口,例如:
.filter(as(String::isEmpty).negate().and("A"::equals))解析所有方法引用
但是,现在仍有一个问题亟待解决。我们不能随随便便地创建一大堆静态as()函数,因为一个方法引用可能解析为多种as()方法,正如本文开头提到的那样。所以,一个更妙的解决方案,是把函数接口类型名添加至每个静态方法,这样我们就可以程序化地为每个函数接口转换方法选择一个特定的方法引用。我们有一个工具类,可以让每个方法引用都转换为Java标准包 `java.util.function中任意匹配的函数接口。
直接在GitHub下载最新版本
import java.util.function.*; * @author Per Minborg public class FunctionCastUtil { public static T, U BiConsumer T, U asBiConsumer(BiConsumer T, U biConsumer) { return biConsumer; public static T, U, R BiFunction T, U, R asBiFunction(BiFunction T, U, R biFunction) { return biFunction; public static T BinaryOperator T asBinaryOperator(BinaryOperator T binaryOperator) { return binaryOperator; public static T, U BiPredicate T, U asBiPredicate(BiPredicate T, U biPredicate) { return biPredicate; public static BooleanSupplier asBooleanSupplier(BooleanSupplier booleanSupplier) { return booleanSupplier; public static T Consumer T asConsumer(Consumer T consumer) { return consumer; public static DoubleBinaryOperator asDoubleBinaryOperator(DoubleBinaryOperator doubleBinaryOperator) { return doubleBinaryOperator; public static DoubleConsumer asDoubleConsumer(DoubleConsumer doubleConsumer) { return doubleConsumer; public static R DoubleFunction R asDoubleFunction(DoubleFunction R doubleFunction) { return doubleFunction; public static DoublePredicate asDoublePredicate(DoublePredicate doublePredicate) { return doublePredicate; public static DoubleToIntFunction asDoubleToIntFunction(DoubleToIntFunction doubleToIntFunctiontem) { return doubleToIntFunctiontem; public static DoubleToLongFunction asDoubleToLongFunction(DoubleToLongFunction doubleToLongFunction) { return doubleToLongFunction; public static DoubleUnaryOperator asDoubleUnaryOperator(DoubleUnaryOperator doubleUnaryOperator) { return doubleUnaryOperator; public static T, R Function T, R asFunction(Function T, R function) { return function; public static IntBinaryOperator asIntBinaryOperator(IntBinaryOperator intBinaryOperator) { return intBinaryOperator; public static IntConsumer asIntConsumer(IntConsumer intConsumer) { return intConsumer; public static R IntFunction R asIntFunction(IntFunction R intFunction) { return intFunction; public static IntPredicate asIntPredicate(IntPredicate intPredicate) { return intPredicate; public static IntSupplier asIntSupplier(IntSupplier intSupplier) { return intSupplier; public static IntToDoubleFunction asIntToDoubleFunction(IntToDoubleFunction intToDoubleFunction) { return intToDoubleFunction; public static IntToLongFunction asIntToLongFunction(IntToLongFunction intToLongFunction) { return intToLongFunction; public static IntUnaryOperator asIntUnaryOperator(IntUnaryOperator intUnaryOperator) { return intUnaryOperator; public static LongBinaryOperator asLongBinaryOperator(LongBinaryOperator longBinaryOperator) { return longBinaryOperator; public static LongConsumer asLongConsumer(LongConsumer longConsumer) { return longConsumer; public static R LongFunction R asLongFunction(LongFunction R longFunction) { return longFunction; public static LongPredicate asLongPredicate(LongPredicate longPredicate) { return longPredicate; public static T LongSupplier asLongSupplier(LongSupplier longSupplier) { return longSupplier; public static LongToDoubleFunction asLongToDoubleFunction(LongToDoubleFunction longToDoubleFunction) { return longToDoubleFunction; public static LongToIntFunction asLongToIntFunction(LongToIntFunction longToIntFunction) { return longToIntFunction; public static LongUnaryOperator asLongUnaryOperator(LongUnaryOperator longUnaryOperator) { return longUnaryOperator; public static T ObjDoubleConsumer T asObjDoubleConsumer(ObjDoubleConsumer T objDoubleConsumer) { return objDoubleConsumer; public static T ObjIntConsumer T asObjIntConsumer(ObjIntConsumer T objIntConsumer) { return objIntConsumer; public static T ObjLongConsumer T asObjLongConsumer(ObjLongConsumer T objLongConsumer) { return objLongConsumer; public static T Predicate T asPredicate(Predicate T predicate) { return predicate; public static T Supplier T asSupplier(Supplier T supplier) { return supplier; public static T, U ToDoubleBiFunction T, U asToDoubleBiFunction(ToDoubleBiFunction T, U toDoubleBiFunction) { return toDoubleBiFunction; public static T ToDoubleFunction T asToDoubleFunction(ToDoubleFunction T toDoubleFunction) { return toDoubleFunction; public static T, U ToIntBiFunction T, U asToIntBiFunction(ToIntBiFunction T, U toIntBiFunction) { return toIntBiFunction; public static T ToIntFunction T asToIntFunction(ToIntFunction T ioIntFunction) { return ioIntFunction; public static T, U ToLongBiFunction T, U asToLongBiFunction(ToLongBiFunction T, U toLongBiFunction) { return toLongBiFunction; public static T ToLongFunction T asToLongFunction(ToLongFunction T toLongFunction) { return toLongFunction; public static T UnaryOperator T asUnaryOperator(UnaryOperator T unaryOperator) { return unaryOperator; private FunctionCastUtil() {
在静态导入了相关方法之后,我们就可以这样写:
Stream.of("A", "", "B").filter(asPredicate(String::isEmpty).negate()).count();一个更好的解决方案
如果函数接口本身就包含一个接收方法引用并将其转换为某类函数接口的静态方法,那就更好了。举例来说,标准的Java Predicated函数接口就会变成这样:
@FunctionalInterface public interface Predicate T { boolean test(T t); default Predicate T and(Predicate ? super T other) {...} default Predicate T negate() {...} default Predicate T or(Predicate ? super T other) {...} static T Predicate T isEqual(Object targetRef) {...} // New proposed support method to return a // Predicate view of a Functional Reference public static T Predicate T of(Predicate T predicate) { return predicate;
因此,我们可以这样写:
Stream.of("A", "", "B").filter(Predicate.of(String::isEmpty).negate()).count();
笔者觉得这样看起来好极了!
快联系离你最近的Open JDK开发人员,提出你的修改建议吧!
本文转自 OneAPM 官方博客
原帖地址:https://dzone.com/articles/put-your-java-8-method-references-to-work
相关文章
- Java应用线上问题排查的常用工具和方法
- Java面向对象基础---名词概念的理解:方法的重载、构造函数、封装性
- c++与java中子类中调用父类成员的方法
- Java实现 LeetCode 438 找到字符串中所有字母异位词
- java实现第七届蓝桥杯打印数字
- java实现第四届蓝桥杯马虎的算式
- java实现第六届蓝桥杯灾后重建
- Java实现第九届蓝桥杯堆的计数
- Java实现斐波那契数列的多种方法
- Java中lang包的常用方法介绍
- Java实现 蓝桥杯VIP 算法提高 色盲的民主
- Java实现 蓝桥杯VIP 算法训练 友好数
- 【JAVA】Java 异常中e的getMessage()和toString()方法的异同
- Effective Java -- 对于所有对象都通用的方法
- Atitit java方法引用(Method References) 与c#委托与脚本语言js的函数指针
- Atitit java方法引用(Method References) 与c#委托与脚本语言js的函数指针
- Atitit. 衡量项目规模 ----包含的类的数量 .net java类库包含多少类 多少个api方法??
- 【反射机制】Java中的反射机制,使用反射机制创建对象、访问属性、方法、构造方法等
- 【java】Java教程
- Java——ThreadLocal概述、解决SimpleDateFormat出现的异常、内存泄漏、弱引用、remove方法
- Java main方法_解释Java中的main方法,及其作用_一个java文件中可包含多个main方法
- Java中Calendar类的常用方法
- Java new关键字和newInstance()方法的区别
- 一行代码, Java 怎样把List 转成 Map 的方法( Java 8 中的Stream API )
- Java并发包中线程池的种类和特点介绍
- Java学习之道:jdk环境变量配置方法
- java设计模式:观察者模式
- java 泛型 -- 泛型类,泛型接口,泛型方法
- 【Java 基础】Java 数组、方法极致精讲
- JAVA--泛型复习
- Java核心类库之(接口组成更新、方法引用、函数式接口)