Lambda表达式
Lambda与匿名内部类对比:
public static void main(String[] args) {
/*使用匿名内部类实现线程*/
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我是匿名内部类!");
}
}).start();
/*使用lambda实现线程*/
new Thread(()->{
System.out.println("我是Lambda");
}).start();
}
Lambda的标准形式:
( 参数类型 参数名称 ) -> {代码体 ;}说明:(参数类型 参数名称)表示参数列表{代码体} 表示方法体->箭头 用来分隔参数列表和方法体
Lambda实现原理总结:
匿名内部类,在程序编译时,会生成一个class文件
Lambda在程序运行时,会生成一个class文件。
具体过程:
1.程序编译中,会先生成一个静态的方法,该方法为接口的实现方法
2.在执行过程中,生成一个匿名内部类,实现接口,重写抽象接口方法
3.在接口重写的方法实际上就是调用编译时生成的静态方法
Lambda的省略格式:
在
Lambda
标准格式的基础上,使用省略写法的规则为:
1.
小括号内参数的类型可以省略
2.
如果小括号内
有且仅有一个参数
,则小括号可以省略
3.
如果大括号内
有且仅有一个语句
,可以同时省略大括号、
return
关键字及语句分号
(int a) -> {return new Person();}
省略后
a->new Person()
Lambda的前提条件:
1.
方法的参数或变量的类型是接口
2.
这个接口中只能有一个抽象方法
函数式接口
函数式接口在
Java
中是指:
有且仅有一个抽象方法的接口
。
函数式接口,即适用于函数式编程场景的接口。而
Java
中的函数式编程体现就是
Lambda
,所以函数式接口就是可以
适用于
Lambda
使用的接口。只有确保接口中有且仅有一个抽象方法,
Java
中的
Lambda
才能顺利地进行推导。
FunctionalInterface
注解
与
@Override
注解的作用类似,
Java 8
中专门为函数式接口引入了一个新的注解:
@FunctionalInterface
。该注解可用于一个接口的定义上:
@FunctionalInterfacepublic interface Operator {void myMethod();}
一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。不过,即
使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。
Lambda和匿名内部类对比:
1.
所需的类型不一样
匿名内部类
,
需要的类型可以是类
,
抽象类
,
接口
Lambda
表达式
,
需要的类型必须是接口
2.
抽象方法的数量不一样
匿名内部类所需的接口中抽象方法的数量随意
Lambda
表达式所需的接口只能有一个抽象方法
3.
实现原理不同
匿名内部类是在编译后会形成
class
Lambda
表达式是在程序运行的时候动态生成
class
常用内置函数式接口
常用内置函数式接口介绍
1. Supplier接口
@FunctionalInterfacepublic interface Supplier<T> {public abstract T get();}
public static void main(String[] args) {
test(()->{
return 10;
});
}
public static int test(Supplier<Integer> supplier){
Integer integer = supplier.get();
System.out.println("获取到的值:"+integer);
return integer;
}
2. Consumer接口
@FunctionalInterfacepublic interface Consumer<T> {public abstract void accept(T t);}
public class Test5 {
public static void main(String[] args) {
test(s->{
System.out.println("我的名字是"+s);
},"小明");
}
public static void test(Consumer<String> consumer,String name){
consumer.accept(name);
}
3.Function接口
@FunctionalInterfacepublic interface Function<T, R> {public abstract R apply(T t);}
public static void main(String[] args) {
test(s -> {return Integer.valueOf(s);},"100");
}
public static void test(Function<String,Integer> function,String number){
Integer n = function.apply(number);
System.out.println("将字符串"+number+"转化成数字"+n);
}
4.Predicate接口
@FunctionalInterfacepublic interface Predicate<T> {public abstract boolean test(T t);}Predicate 接口用于做判断 , 返回 boolean 类型的值
public static void main(String[] args) {
test(s->{
return s.contains("n");
},"name");
}
public static void test(Predicate<String> predicate,String name){
System.out.println(predicate.test(name));
}
Lambda表达式方法的引用
方法引用格式:
符号表示 ::
符号说明:双冒号为方法引用运算符,而它所在的表达式被称为方法引用。
应用场景:如果Lambda所要实现的方案 , 已经有其他方法存在相同方案,那么则可以使用方法引用。
常见的引用方式:
1.
instanceName::methodName
对象
::
方法名
2.
ClassName::staticMethodName
类名
::
静态方法
3.
ClassName::methodName
类名
::
普通方法
4.
ClassName::new
类名
::new
调用的构造器
5.
TypeName[]::new
String[]::new
调用数组的构造器
1. instanceName::methodName 对象::方法名
public class Test2 {
public String test(){
return "我是对象::方法的引用";
}
public static void main(String[] args) {
Test2 test2=new Test2();
//方法调用
Supplier<String> supplier2=()->{
return test2.test();
};
System.out.println(supplier2.get());
//方法引用
Supplier<String> supplier=test2::test;
System.out.println(supplier.get());
}
}
2. ClassName::staticMethodName 类名::静态方法
public class Test3 {
public static String test(){
return "我是类名引用静态方法";
}
public static void main(String[] args) {
//方法调用
Supplier<String> supplier=()->{
return Test3.test();
};
System.out.println(supplier.get());
//方法引用
Supplier<String> supplier1=Test3::test;
System.out.println(supplier1.get());
}
3. ClassName::methodName 类名::普通方法
public class Test4 {
public static void main(String[] args) {
//方法调用
Function<String,Integer> function=(s->{
return s.length();
});
System.out.println(function.apply("abc"));
//方法引用 类名引用普通方式,实际上是将第一个参数作为方法的调用者
Function<String,Integer> function1=String::length;
System.out.println(function1.apply("abc"));
BiFunction<String,Integer,String> biFunction=((s1,s2)->{
return s1.substring(s2);
});
System.out.println(biFunction.apply("abcd",2));
BiFunction<String,Integer,String> biFunction1=String::substring;
System.out.println(biFunction1.apply("abcd",2));
}
}
4. ClassName::new 类名::new 调用的构造器
public class Test5 {
public Test5() {
System.out.println("我被构造出来了");
}
public static void main(String[] args) {
//方法调用
Supplier<Test5> supplier=()->{
return new Test5();
};
System.out.println(supplier.get());
//方法引用
Supplier<Test5> supplier1=Test5::new;
System.out.println(supplier1.get());
}
}
5. TypeName[]::new String[]::new 调用数组的构造器
public class Test6 {
public static void main(String[] args) {
//方法调用
Function<Integer,String[]> function=(len)->{
return new String[len];
};
String[] apply = function.apply(10);
System.out.println(apply.length);
//方法引用
Function<Integer,String[]> function1=String[]::new;
String[] apply1 = function1.apply(10);
System.out.println(apply1.length);
}
}
Stream流
Stream流的思想概述:
Stream
流式思想类似于工厂车间的
“
生产流水线
”
,
Stream
流不是一种数据结构,不保存数据,而是对数据进行加工
处理。
Stream
可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
Stream API
能让我们快速完成许多复杂的操作,如筛选、切片、映射、查找、去除重复,统计,匹配和归约
获取Stream流的两种方式
1.根据Collection获取流
public interface Collection {default Stream<E> stream()}
2.根据Stream中的静态方法of获取流
public static void main(String[] args) {
// Stream中的静态方法: static Stream of(T... values)
Stream<String> stream6 = Stream.of("aa", "bb", "cc");
String[] arr = {"aa", "bb", "cc"};
Stream<String> stream7 = Stream.of(arr);
Integer[] arr2 = {11, 22, 33};
Stream<Integer> stream8 = Stream.of(arr2);
// 注意:基本数据类型的数组不行
int[] arr3 = {11, 22, 33};
Stream<int[]> stream9 = Stream.of(arr3);
}
Stream常用方法
Stream流的注意事项:
1. Stream
只能操作一次
2. Stream
方法返回的是新的流
3. Stream
不调用终结方法,中间的操作不会执行