3

WebFlux 前置知识(二)

 3 years ago
source link: http://www.javaboy.org/2021/0525/method-reference.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

先来说说上周送书的事。小伙伴们太热情了,留言有 300 多条,但是受微信限制,只能精选100条,所以还有很多小伙伴的留言没有精选,但是松哥都认真看了,还请小伙伴们理解。每次留言送书都会选择困难,不知道书送给谁,每个人我都想送,但是只有六本。。。不停的纠结,每次选幸运小伙伴都要花费一小时以上,下次送书还是考虑抽奖吧,省事哈哈。

上篇文章和小伙伴们聊了 Lambda 表达式和函数接口,今天我们一起来看下 方法引用、变量引用、类型推断以及级联表达式四个点,也算是 WebFlux 的一个前置知识点。

废话不多说,开整。

1 方法引用

到今天,方法引用估计很多小伙伴可能多多少少都见过,即使自己没写过,可能也看别人写过。用过的小伙伴可能会感觉这个用着真爽,cool!没用过的小伙伴可能就要吐槽这什么鬼代码。

不管怎么样,我们今天还是来看看方法引用,也算是我们学习 WebFlux 的一个前置知识。

1.1 什么是方法引用

什么是方法引用?

简单说,方法引用就是一个 Lambda 表达式,操作符就是 ::,有的小伙伴们可能会觉得所谓的 Lambda 就是 -> 代替匿名内部类,其实不然!Lambda 中包含的东西还是蛮多的,方法引用就算是其中之一。

有的时候,我们使用 Lambda,需要自己写方法的实现,但是有的时候,我们可能不需要自己写方法的实现,就是单纯的调用一下方法,这种时候,通过方法名称来调用,会更加清晰,可读性更高,也更加简洁易懂。

方法引用不仅可以用来访问类或者实例中已经存在的方法,也可以用来访问构造方法。

2.2 四种方法引用

2.2.1 静态方法引用

例如我定义一个 Lambda,该 Lambda 中有一个方法可以完成对数字格式的转换,如下:

public class LambdaDemo05 {
public static void main(String[] args) {
Function<Integer, String> func = a -> String.valueOf(a);
String s = func.apply(99);
System.out.println("s = " + s);
}
}

在上面的这个 Function 中,我们将一个 Integer 类型数字转为了一个字符串,由于在 Lambda 中并没有其他代码,就是一个简单的类型转换,因为我们可以将之简写成如下方式:

public class LambdaDemo05 {
public static void main(String[] args) {
Function<Integer, String> func = String::valueOf;
String s = func.apply(99);
System.out.println("s = " + s);
}
}

类似的,比如我们有一个 Consumer,如下:

Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("javaboy");

Consumer 消费一个字符串,消费的方式就是控制台打印,这种时候我们可以简写成如下方式:

Consumer<String> consumer = System.out::println;
consumer.accept("javaboy");

这就是静态方法引用。

再举个例子,用 Lambda 写一个给参数求次幂的函数,如下:

BiFunction<Integer, Integer, Double> func = (a, b) -> Math.pow(a, b);
Double result = func.apply(3, 4);
System.out.println("result = " + result);

传入两个参数类型都是 Integer,返回的数据类型是 Double,调用 Math.pow 计算次幂。

上面这段代码我们也可以通过静态方法引用简化:

BiFunction<Integer, Integer, Double> func = Math::pow;
Double result = func.apply(3, 4);
System.out.println("result = " + result);

2.2.2 实例方法引用

方法引用也可以用在实例方法上。

Random random = new Random();
IntUnaryOperator func = i -> random.nextInt(i);
Integer r = func.applyAsInt(10);
System.out.println("r = " + r);

这段代码也可以使用方法引用,方式如下:

Random random = new Random();
IntUnaryOperator func = random::nextInt;
Integer r = func.applyAsInt(10);
System.out.println("r = " + r);

就是把类换成实例而已,其他都是一样的。

不过需要注意的是,字符串的实例稍微特殊一些,如下一个字符串排序方法:

String[] stringArray = {"Barbara", "Mary", "James"};
Arrays.sort(stringArray,String.CASE_INSENSITIVE_ORDER);
System.out.println(Arrays.toString(stringArray));

如果使用方法引用,方式如下:

String[] stringArray = {"Barbara", "Mary", "James"};
Arrays.sort(stringArray, String::compareToIgnoreCase);
System.out.println(Arrays.toString(stringArray));

这个感觉有点像静态方法引用,其实不是的,Lambda 的第一个参数会成为调用实例方法的对象。

在实例方法引用中,如果需要指定泛型,泛型放在 :: 后面。

2.2.3 构造方法引用

例如如下方法提供一个 Cat 实例:

Supplier<Cat> supplier = () -> new Cat();
Cat cat = supplier.get();

通过方法引用,可以简写成如下形式:

Supplier<Cat> supplier = Cat::new;
Cat cat = supplier.get();

2.2.4 数组构造方法引用

例如创建一个长度为 10 的数组,如下:

IntFunction<int[]> func = (i) -> new int[i];
int[] arr = func.apply(10);
System.out.println("arr.length = " + arr.length);

使用构造方法引用,可以简写成如下方式:

IntFunction<int[]> func = int[]::new;
int[] arr = func.apply(10);
System.out.println("arr.length = " + arr.length);

3. 变量引用

内部类中使用外部定义的变量,需要这个变量是一个 final 类型的,如果用了 Lambda 表达式,这个规则依然适用。

String s = "javaboy";
Consumer<String> consumer = s1 -> System.out.println(s1 + s);
consumer.accept("hello ");

此时虽然不用给 s 变量添加 final 标记,但是它实际上已经是 final 类型的了,如果强行修改,就会报错:

4. 类型推断

大部分情况下,Lambda 表达式都是可以推断出自己的类型的,个别情况下可能推断不出,比如出现方法重载的时候,这个时候可能就需要我们类型强转了,例如如下代码:

@FunctionalInterface
interface ICalculator2{
int add(int a, int b);
}
@FunctionalInterface
interface ICalculator3{
int multiply(int a, int b);
}
public class LambdaDemo06 {
public static void main(String[] args) {
calculator((ICalculator2) (a, b) -> a + b);
}

public static void calculator(ICalculator2 iCalculator) {
}
public static void calculator(ICalculator3 iCalculator) {
}
}

上面的代码中定义了两个计算器 ICalculator2 和 ICalculator3,然后有一个重载的方法分别用到了 ICalculator2 和 ICalculator3,这就导致在使用 Lambda 表达式时无法推断出到底使用哪个对象,此时我们就需要显式的进行类型强转。

5. 级联表达式

Lambda 表达式也可以写成 N 多层,具体则看需求。

例如三个数相加,可以写成如下形式:

Function<Integer, Function<Integer, IntFunction<Integer>>> func = x -> y -> z -> x + y + z;
Integer i = func.apply(3).apply(4).apply(5);
System.out.println("i = " + i);

这个表达式从右往左看可能容易理解。

z->x+y+z 对应的是 IntFunction<Integer>

z->x+y+z 整体作为返回,y 作为输入,对应的是 Function<Integer, IntFunction<Integer>>

y -> z -> x + y + z 整体作为返回,x 作为输入,对应的就是 Function<Integer, Function<Integer, IntFunction<Integer>>>

that’s all。

参考资料:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK