【编者按】本文作者为拥有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 能为您提供端到端的 Java 应用性能解决方案,我们支持所有常见的 Java 框架及应用服务器,助您快速发现系统瓶颈,定位异常根本原因。分钟级部署,即刻体验,Java 监控从来没有如此简单。想阅读更多技术文章,请访问 OneAPM 官方技术博客

本文转自 OneAPM 官方博客

原帖地址:https://dzone.com/articles/put-your-java-8-method-references-to-work

Java 8方法引用使用指南的更多相关文章

  1. Java 8 新特性-菜鸟教程 (2) -Java 8 方法引用

    Java 8 方法引用 方法引用通过方法的名字来指向一个方法. 方法引用可以使语言的构造更紧凑简洁,减少冗余代码. 方法引用使用一对冒号 :: . 下面,我们在 Car 类中定义了 4 个方法作为例子 ...

  2. JAVA 8 方法引用 - Method References

    什么是方法引用 简单地说,就是一个Lambda表达式.在Java 8中,我们会使用Lambda表达式创建匿名方法,但是有时候,我们的Lambda表达式可能仅仅调用一个已存在的方法,而不做任何其它事,对 ...

  3. Java 8 方法引用

    转自:https://www.runoob.com/java/java8-method-references.html 方法引用通过方法的名字来指向一个方法. 方法引用可以使语言的构造更紧凑简洁,减少 ...

  4. Java 之 方法引用

    方法引用 一.冗余的Lambda场景 来看一个简单的函数式接口以应用Lambda表达式: @FunctionalInterface public interface Printable { void ...

  5. Java(43)JDK新特性之方法引用

    作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15228461.html 博客主页:https://www.cnblogs.com/testero ...

  6. 深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)

    作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout-language- ...

  7. Java 8函数编程轻松入门(四)方法引用

    C#中系统提供了许多IEnumerable的扩展方法.同样在Java 8中新引入了Collector类. 1.方法引用 定义: 简而言之:就是一个Lambda表达式.在Java 8中,我们我们会使用L ...

  8. Java笔记——Java8特性之Lambda、方法引用和Streams

    Java8已经推出了好一段时间了,而掌握Java8的新特性也是必要的,如果要进行Spring开发,那么可以发现Spring的官网已经全部使用Java8来编写示例代码了,所以,不学就看不懂. 这里涉及三 ...

  9. Java:方法的参数是传值还是传引用

    Java中方法的参数总是采用传值的方式. 下列方法欲实现对象的交换,但实际上是不能实现的. public void swap(simpleClass a,simpleClass b){ simpleC ...

随机推荐

  1. rack简介

    什么是rack rack是对ruby的Net::HTTP进行封装了的包,使用rack能够方便的新建一个简单的web应用. what is rack Rack describes itself as f ...

  2. Postman安装及简介

    Postman简介 不管web自动化测试还是APP自动化端,测试过程中都会涉及到接口测试.接口测试分为服务器端测试和客户端测试.今天给大家介绍一个测试服务器端的小工具--Postman.它可以构造各类 ...

  3. gensim学习笔记

    1.词向量建模的word2vec模型和用于长文本向量建模的doc2vec模型 在Gensim中实现word2vec模型非常简单.首先,我们需要将原始的训练语料转化成一个sentence的迭代器:每一次 ...

  4. seq2seq attention

    1.seq2seq:分为encoder和decoder a.在decoder中,第一时刻输入的是上encoder最后一时刻的状态,如果用了双向的rnn,那么一般使用逆序的最后一个时刻的输出(网上说实验 ...

  5. Java 注解实例

    package com.annotation; import java.lang.annotation.Retention; import java.lang.annotation.Target; i ...

  6. Mysql——权限管理

    安装Mysql时会自动安装一个名为mysql的数据库.这个数据库下面存储的是权限表. mysql> show databases; +--------------------+ | Databa ...

  7. 【转】Js获取当前日期时间及其它操作

    Js获取当前日期时间及其它操作 原文地址:http://www.cnblogs.com/carekee/articles/1678041.html var myDate = new Date();my ...

  8. JVM读书笔记

    1 概念 java virtual machine为java虚拟机,运行使用jdk中编译器编译的java程序. 2 JVM内存模型 程序计数器:线程私有.当前线程正在执行的行号指示器. Java虚拟机 ...

  9. IDEA 中将已有项目放到 GitHub 上去

    前言:公司用的都是 idea+svn 来管理代码,所以家里尝试一下 idea+github 来管理.我的本地仓库是配置过ssh key,所以此处没有再次配置ssh key,如需配置可以参考 https ...

  10. js-权威指南学习笔记18

    1.除mouseenter和mouseleave外的所有鼠标事件都能冒泡. 2.传递给鼠标事件处理程序的事件对象有clientX和clientY属性,它们制订了鼠标指针相对于包含窗口的坐标. 3.一个 ...