松哥原创的 Spring Boot 视频教程已经杀青,感兴趣的小伙伴戳这里-->Spring Boot Vue 微人事视频教程
先来说说上周送书的事。小伙伴们太热情了,留言有 300 多条,但是受微信限制,只能精选100条,所以还有很多小伙伴的留言没有精选,但是松哥都认真看了,还请小伙伴们理解。每次留言送书都会选择困难,不知道书送给谁,每个人我都想送,但是只有六本。。。不停的纠结,每次选幸运小伙伴都要花费一小时以上,下次送书还是考虑抽奖吧,省事哈哈。
上篇文章和小伙伴们聊了 Lambda 表达式和函数接口,今天我们一起来看下 方法引用、变量引用、类型推断以及级联表达式四个点,也算是 WebFlux 的一个前置知识点。
废话不多说,开整。
1 方法引用
到今天,方法引用估计很多小伙伴可能多多少少都见过,即使自己没写过,可能也看别人写过。用过的小伙伴可能会感觉这个用着真爽,cool!没用过的小伙伴可能就要吐槽这什么鬼代码。
不管怎么样,我们今天还是来看看方法引用,也算是我们学习 WebFlux 的一个前置知识。
1.1 什么是方法引用
什么是方法引用?
简单说,方法引用就是一个 Lambda 表达式,操作符就是 ::
,有的小伙伴们可能会觉得所谓的 Lambda 就是 ->
代替匿名内部类,其实不然!Lambda 中包含的东西还是蛮多的,方法引用就算是其中之一。
有的时候,我们使用 Lambda,需要自己写方法的实现,但是有的时候,我们可能不需要自己写方法的实现,就是单纯的调用一下方法,这种时候,通过方法名称来调用,会更加清晰,可读性更高,也更加简洁易懂。
方法引用不仅可以用来访问类或者实例中已经存在的方法,也可以用来访问构造方法。
2.2 四种方法引用
2.2.1 静态方法引用
例如我定义一个 Lambda,该 Lambda 中有一个方法可以完成对数字格式的转换,如下:
代码语言:javascript复制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 中并没有其他代码,就是一个简单的类型转换,因为我们可以将之简写成如下方式:
代码语言:javascript复制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,如下:
代码语言:javascript复制Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("javaboy");
Consumer 消费一个字符串,消费的方式就是控制台打印,这种时候我们可以简写成如下方式:
代码语言:javascript复制Consumer<String> consumer = System.out::println;
consumer.accept("javaboy");
这就是静态方法引用。
再举个例子,用 Lambda 写一个给参数求次幂的函数,如下:
代码语言:javascript复制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 计算次幂。
上面这段代码我们也可以通过静态方法引用简化:
代码语言:javascript复制BiFunction<Integer, Integer, Double> func = Math::pow;
Double result = func.apply(3, 4);
System.out.println("result = " result);
2.2.2 实例方法引用
方法引用也可以用在实例方法上。
代码语言:javascript复制Random random = new Random();
IntUnaryOperator func = i -> random.nextInt(i);
Integer r = func.applyAsInt(10);
System.out.println("r = " r);
这段代码也可以使用方法引用,方式如下:
代码语言:javascript复制Random random = new Random();
IntUnaryOperator func = random::nextInt;
Integer r = func.applyAsInt(10);
System.out.println("r = " r);
就是把类换成实例而已,其他都是一样的。
不过需要注意的是,字符串的实例稍微特殊一些,如下一个字符串排序方法:
代码语言:javascript复制String[] stringArray = {"Barbara", "Mary", "James"};
Arrays.sort(stringArray,String.CASE_INSENSITIVE_ORDER);
System.out.println(Arrays.toString(stringArray));
如果使用方法引用,方式如下:
代码语言:javascript复制String[] stringArray = {"Barbara", "Mary", "James"};
Arrays.sort(stringArray, String::compareToIgnoreCase);
System.out.println(Arrays.toString(stringArray));
这个感觉有点像静态方法引用,其实不是的,Lambda 的第一个参数会成为调用实例方法的对象。
在实例方法引用中,如果需要指定泛型,泛型放在 ::
后面。
2.2.3 构造方法引用
例如如下方法提供一个 Cat 实例:
代码语言:javascript复制Supplier<Cat> supplier = () -> new Cat();
Cat cat = supplier.get();
通过方法引用,可以简写成如下形式:
代码语言:javascript复制Supplier<Cat> supplier = Cat::new;
Cat cat = supplier.get();
2.2.4 数组构造方法引用
例如创建一个长度为 10 的数组,如下:
代码语言:javascript复制IntFunction<int[]> func = (i) -> new int[i];
int[] arr = func.apply(10);
System.out.println("arr.length = " arr.length);
使用构造方法引用,可以简写成如下方式:
代码语言:javascript复制IntFunction<int[]> func = int[]::new;
int[] arr = func.apply(10);
System.out.println("arr.length = " arr.length);
3. 变量引用
内部类中使用外部定义的变量,需要这个变量是一个 final 类型的,如果用了 Lambda 表达式,这个规则依然适用。
如下;
代码语言:javascript复制String s = "javaboy";
Consumer<String> consumer = s1 -> System.out.println(s1 s);
consumer.accept("hello ");
此时虽然不用给 s 变量添加 final 标记,但是它实际上已经是 final 类型的了,如果强行修改,就会报错:
4. 类型推断
大部分情况下,Lambda 表达式都是可以推断出自己的类型的,个别情况下可能推断不出,比如出现方法重载的时候,这个时候可能就需要我们类型强转了,例如如下代码:
代码语言:javascript复制@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 多层,具体则看需求。
例如三个数相加,可以写成如下形式:
代码语言:javascript复制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。
参考资料:
- https://blog.csdn.net/sun_promise/article/details/51190256
- https://www.jianshu.com/p/4a3da6a11b58