System.out::println是什么 ? Lambda表达式和方法引用
System.out::printlin 可以很好的串联Java8新特性中的Lambda表达式和方法引用
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
//完成对集合元素的遍历输出
list.forEach(System.out::println);
- 首先用Lambda体简化匿名内部类
- 了解函数式接口的概念
- 方法引用的用法
- Consumer<T> 的场景
- 引出函数式编程
- System.out::println
链条式引出结果, 并非完整文档笔记.
从匿名内部类 到 Lambda
Lambda本质就是一种书写方式 , 他的整体就是一个对象
匿名内部类写法
假如要创建接口对象, 一种方式就是写成匿名内部类 ,
这里匿名的说法, 描述的是不用写一个类来继承Runnable , 进而new对象再去调用这个类重写的run方法
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("这是runnable");
}
};
Lambda写法
. 匿名内部类这种方式过于复杂, 可以写成Lambda的格式进行优化
Runnable runnable1 = () -> {
System.out.println("这是runnable2");
};
相当于将 new Runnable() 和 除了方法内的代码给省略
( ) 就是run( ) 的参数表, 接口名方法名都省了.
更简化的Lambda
当这个方法体中只有一行代码时, 可以直接省略{}括号
Runnable runnable2 = () -> System.out.println("这是runnable2");
所以Lambda体在Java中是对象
在其它语言中Lambda体或者称之为箭头函数./回调函数, 但在Java中很明显Lambda体是对象, 就是函数式接口的一个对象
( ) 就是接口方法的参数
-> 箭头就是参数要传给谁
方法体, 就是要接受参数并实现的逻辑
但runnable的run方法是一个无参方法,
- 当无参方法时, 写成 ( ) -> 方法体
- 当有且只有一个方法参数时写成 arg -> 方法体
- 当有多个方法参数时 写成 (arg1,arg2) -> 方法体
函数式接口
函数式接口的定义
只有一个方法的接口为函数式接口 , 类似与线程先关的Runnable 接口, 只有一个run( ) 方法 他就是一个函数式接口 ,
而且接口还有函数式接口检查注解@FunctionalInterface, 来用于检查这个接口是不是函数式接口
函数式编程
函数式接口的对象是Lambda体,
函数式编程就是是像传递参数一样传递一个Lambda体
举例Thread类, 打开Java中的Threa类 , 发现有一个构造方法, 需要一个参数为Runnable类型
具体写法就是
Runnable runnable3 = () -> System.out.println("这是runnable3");
//new Thread(Runnable r)
Thread thread = new Thread(runnable3);
thread.start();//结果 : 这是runnable3
这就是实现了函数式编程, 传递过去的参数本质上是一个对象, 对象是Runnable接口的一个实例
函数式接口的分类
Java中内置了4大类型的函数式接口来适应不同的场景, 主要4种但不限于4种,而且还有众多的子接口
- Comsumer<T> accept( T ,t ) 只消费传进来的参数没有返回值
- Supplier<T> T get() 不接受参数有返回
- Function<T,R> R apply (T, t) 对传进来的对象t操作, 返回R类型
- Predicate<T> boolean test(T t)
Consumer 用法
Consumer<String> 表明了accept 方法的参数 为String类型, 就是要把传进去的String类型参数给消费掉
诸如"匿名内部类写法" “Lambda写法”
//匿名内部类写法
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("匿名内部类写法");
//Lambda写法
Consumer<String> consumer1 = s -> System.out.println(s);
consumer1.accept("Lambda写法");
Consumer在函数式编程中的使用
例如 集合中的forEach方法就是 一个函数式编程的实现 , 最终也将引出System.out::println
方法要传入一个 Comsumer<T> 的对象
//创建一个集合
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
// 这里是完整写法 , 可以吧consumer3直接传进去, 也可以直接写简化版Lambda;
Consumer<Integer> consumer = new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
};
//将Lambda体传入 , 也就是函数式编程 , 完成对集合的便利
list.forEach( s -> System.out.println(s));
其实在forEach方法中, 接受到 函数式接口对象, 进行非空判断之后, 也是调用对象里的accept方法对每一个元素进行输出操作
距离System.out::println仅差一步
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
//将Lambda体传入 , 也就是函数式编程 , 完成对集合的便利
list.forEach( s -> System.out.println(s));
//两种方式等同, 就是方法引用对Lambda表达式的再度简化
list.forEach(System.out::println)
方法引用
System.out::println 就是一种方法引用的写法
方法引用的条件是 函数式接口的方法参数类型和返回值类型, 与 方法引用的参数类型和方法引用类型相同
则Lambda体可以简化书写为
对象::方法 类似于System.out::println
类:: 静态方法 类似于 Math::round
- 解读
以list.forEach(Consumer<T> com) 为例子
函数式接口的accetpt方法 参数类型Integer并且无返回值
方法体中的println()方法参数类型也是Integer也无返回值
这种场景下省略参数写法, 直接用对象::方法 , 即 System.out::println
这里Syetem.out 等用于一个PrintStream对象 , 用对象::方法
//创建一个集合
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Consumer<Integer> consumer = new Consumer<Integer>() {
@Override
// accect方法参数类型为Integer, 无返回值
public void accept(Integer integer) {
// System.out 是一个PrintStream对象 , 调用println()方法 该方法参数类型也是Integer也无返回值
System.out.println(integer);
}
};
//所以这时候不仅可以写成lambda体
list.forEach( s -> System.out.println(s));
//也可以使用方法引用 对象::方法
list.forEach(System.out::println)
实现细节
- list集合是ArrayList的对象, 其顶层接口Collection继承自Iterable接口, 所以可以使用其forEach方法
这里forEach 约定参数Consumer的泛型下限为Integer, 因为调用这个方法的实例对象就是Integer类型的集合
this就是对象本身, 进行便利 , 调用accept对便利出的数字进行输出.
在System.out 获得的PrintStream对象的引用 , 可以重载到Int输出的方法 , 可以发现参数类型于accept都是Integer,
返回值都是void , 这样就达成方法引用先决条件
总结反思
Lambda表达式实现了代码简化, 也更加实际的完成了函数式编程 ,
假如使用匿名内部类方式以下代码就会报错
List<String> list = Arrays.asList("1", "2", "3", "4", "5");
Consumer<Integer> consumer = new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
};
//报错
list.forEach(consumer);
//正常使用
list.forEach(System.out::println);
如果使用匿名内部类, Consumer的类型要进行声明, 需要比操作的对象需要什么类型的Consumer都要指定好
那么假如调用类型不固定, 由Integer变成了String , 那么就会出问题. 使用Lambda表达式其类型为自动推导
调用者类型即为Consumer<T> T的类型, 更加灵活.
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!