编写Lambda表达式作为方法引用

编写Lambda表达式作为方法引用

您看到,lambda表达式实际上是方法的实现:函数式接口的唯一抽象方法。有时人们称这些lambda表达式为“匿名方法”,因为它只是:一个没有名称的方法,你可以在应用程序中传递,存储在一个字段或变量中,作为参数传递给一个方法或构造函数,并从一个方法返回。

有时你会编写lambda表达式,只是调用在其他地方定义的特定方法。事实上,当你写下面的代码时,你已经做到了:

Consumer<String> printer = s -> System.out.println(s);

这样写,这个lambda表达式只是在System.out上定义的println()方法的引用

这就是方法引用语法的作用所在。有时,lambda表达式只是对现有方法的引用。在这种情况下,你可以把它写成一个方法引用。然后前面的代码变成了下面的代码:

Consumer<String> printer = System.out::println;

方法引用有四种类型:

  • 静态方法引用
  • 绑定方法引用
  • 未绑定的方法引用
  • 构造函数方法引用

printer consumer 属于未绑定方法引用类别。

大多数情况下,IDE都能够告诉您某个特定的lambda表达式是否可以写成lambda表达式。不要犹豫,尽管问吧!

编写静态方法引用

假设你有以下代码:

DoubleUnaryOperator sqrt = a -> Math.sqrt(a);

这个lambda表达式实际上是对静态方法Math.sqrt()的引用。可以这样写:

DoubleUnaryOperator sqrt = Math::sqrt;

这个特定的方法引用引用了一个静态方法,因此称为静态方法引用。静态方法引用的一般语法是RefType::staticMethod。

静态方法引用可以接受多个参数。看以下代码:

IntBinaryOperator max = (a, b) -> Integer.max(a, b);

你可以用方法引用重写它:

IntBinaryOperator max = Integer::max;

编写未绑定的方法引用
不接受任何参数的方法

假设你有以下代码:

Function<String, Integer> toLength = s -> s.length();

这个function (可以写成ToIntFunction)只是对String类的length()方法的引用。所以你可以把它写成方法引用:

Function<String, Integer> toLength = String::length;

这种语法一开始可能会让人感到困惑,因为它实际上看起来像一个静态调用。但实际上并非如此:length()方法是String类的一个实例方法。

您可以使用这样的方法引用从普通Java bean调用任何getter。假设用户类上定义了getName()。然后可以编写以下函数:

Function<User, String> getName = user -> user.getName();

作为以下方法的参考:

Function<String, Integer> toLength = User::getName;

不接受任何参数的方法

这是你已经看到的另一个例子:

BiFunction<String, String, Integer> indexOf = (sentence, word) -> sentence.indexOf(word);

这个lambda实际上是对String类的indexOf()方法的引用,因此可以写成以下方法引用:

BiFunction<String, String, Integer> indexOf = String::indexOf;

这个语法看起来可能比简单的String::length或User::getName更容易混淆。在脑海中重构以经典方式编写的lambda的一个好方法是检查这个方法引用的类型。这会给你这个lambda 的参数。

非绑定方法引用的一般语法如下:RefType:instanceMethod,其中RefType是类型的名称,instanceMethod是实例方法的名称。

编写绑定方法引用

你看到的第一个方法引用的例子如下:

Consumer<String> printer = System.out::println;

此方法引用称为绑定方法引用。这个方法引用被称为绑定的,因为调用方法的对象是在方法引用本身中定义的。所以这个调用被绑定到方法引用中给出的对象。

如果考虑未绑定语法:Person::getName,可以看到调用方法的对象不是该语法的一部分:它是作为lambda表达式的参数提供的。看以下代码:

Function<User, String> getName = User::getName;
User anna = new User("Anna");
String name = getName.apply(anna);

您可以看到,该函数应用于User的一个特定实例,该实例被传递给该函数。然后该函数对该实例进行操作。

这不是前面的consumer 示例中的情况:在System.out对象上调用println()方法,这是方法引用的一部分。

绑定方法引用的一般语法如下:expr:instanceMethod,其中expr是返回对象的表达式,而instanceMethod是实例方法的名称。

编写构造函数方法引用

您需要知道的最后一种方法引用是构造函数方法引用。假设你有以下Supplier<List>:

Supplier<List<String>> newListOfStrings = () -> new ArrayList<>();

你可以像其他的一样看到这一点:这可以归结为ArrayList的空构造函数的引用。方法引用可以做到这一点。但由于构造函数不是方法,这是另一类方法引用。语法如下:

Supplier<List<String>> newListOfStrings = ArrayList::new;

您可以注意到这里不需要菱形运算符。如果你想把它,那么你还需要提供类型:

Supplier<List<String>> newListOfStrings = ArrayList<String>::new;

您需要意识到这样一个事实:如果不知道方法引用的类型,那么您就不能确切地知道它做什么。下面是一个例子:

Supplier<List<String>> newListOfStrings = () -> new ArrayList<>();
Function<Integer, List<String>> newListOfNStrings = size -> new ArrayList<>(size);

变量newListOfStrings和newListOfNStrings都可以用相同的语法ArrayList::new编写,但它们引用的不是同一个构造函数。你需要小心点。

包装方法引用

下面是四种类型的方法引用。

名称语法lambda表达式
StaticRefType::staticMethod(args) -> RefType.staticMethod(args)
Boundexpr::instanceMethod(args) -> expr.instanceMethod(args)
UnboundRefType::instanceMethod(arg0, rest) -> arg0.instanceMethod(rest)
ConstructorClassName::new(args) -> new ClassName(args)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值