Lambda表达式
JDK8中增加,很多语言都支持Lambda表达式,Java中Lambda表达式其实就是实现SAM接口的语法糖,使得Java也算是支持函数式编程的语言,Lambda表达式写得好极大的减少了代码的冗余,同时可读性也更好。
Lambda表达式
Java8中引入了一个新的操作符“ -> ”,该操作符称为箭头操作符,或者Lambda操作符
- 箭头操作符将Lambda体分为两个部分:
- 左侧:Lambda表达式的参数列表
- 右侧:Lambda表达式中需要执行的功能,即Lambda体
Lambda表达式的几种格式
(参数列表) -> {
Lambda体
}
无参无返回值
public class Test1 {
public static void main(String[] args) {
//原始写法,是用匿名内部类实现Runnable接口
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类");
}
}).start();
//无参无返回值的Lambda表达式写法
new Thread(()-> System.out.println("lambda表达式")).start();
}
}
有参无返回值
- Lambda表达式的参数列表的数据类型是可以省略的,JVM编译器可以通过上下文推断出数据类型(类型推断)
- Lambda表达式中如果只有一个参数,那么()也可以省略不写
public class Test2 {
public static void main(String[] args) {
List<String> list = Arrays.asList("a","bb","ccc");
//原始写法
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//有参无返回值的Lambda表达式
list.forEach(s -> System.out.println("--"+s));
//两个参数:
HashMap<Integer,String> map = new HashMap<>();
map.put(1,"aaa");
map.put(2,"bbb");
map.forEach((k,v)-> System.out.println(k+"-"+v));
}
}
无参有返回值
- 无参一定要有()
- 如果Lambda体中只有一条语句,那么return和{}都可以不写(如果有一条以上语句,必须写return和{},形成一个代码块)
public class Test3 {
public static void main(String[] args) {
//产生一个无限流
Stream<Double> stream = Stream.generate(()->{
return Math.random();
});
stream.forEach((d)-> System.out.println(d));
//只有一条语句时可以省略,如:
//Stream<Double> stream = Stream.generate(()->Math.random());
}
}
有参有返回值
public class Test4 {
public static void main(String[] args) {
//有参有返回值
HashMap<String,Student> map = new HashMap<>();
map.put("zs",new Student(1001,"张三",1000));
map.put("ls",new Student(1002,"李四",900));
map.put("ww",new Student(1003,"王五",800));
//替换集合中student中money小于一千的(将其替换为指定值后返回)
map.replaceAll((k,v)->{
if (v.getMoney()<1000){
v.setMoney(0);
}
return v;
});
map.forEach((k,v)-> System.out.println(k+" "+v));
}
}
方法引用
方法引用是Lambda表达式的另一种表现形式
若Lambda体中的内容已经有方法实现了,那么就可以使用方法引用
要注意:
函数式接口的参数列表和返回值,要与该方法的参数列表和返回值相同
Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用 ClassName::method
- 对象::实例方法名;
PrintStream ps1 = System.out;
Consumer<String> con1 = (x)-> ps1.println(x);
//方法引用
PrintStream ps2 = System.out;
Consumer<String> con2 = ps2::println;
con2.accept("abc123");
//Consumer接口中的accept()方法和PrintStream中的println方法参数列表和返回值相同,所以可以使用方法引用
//上面这样写法是为了能看明白,正常写法:
Consumer<String> con3 = System.out::println;
- 类::静态方法名;
Comparator<Integer> com1 = (x,y)-> Integer.compare(x,y);
//使用方法引用
Comparator<Integer> com2 = Integer::compare;
- 类::实例方法名;
//为了实现给首字母排序时不区分大小写,重写比较器方法
String[] arr = {"aaa","Sss","sda"};
Arrays.sort(arr,(s1,s2)->{return s1.compareToIgnoreCase(s2);});
System.out.println(Arrays.toString(arr));
//使用方法引用
Arrays.sort(arr,String::compareToIgnoreCase);
System.out.println(Arrays.toString(arr));
构造器引用
- 类::new;
需要调用的构造器的参数列表要与函数式接口中的抽象方法的参数列表保持一致
//使用Lambda表达式
Supplier<Student> sup = () -> new Student();
//方法引用 调用无参的构造器,取决于Supplier中的方法“get()”
Supplier<Student> sup2 = Student::new;
//方法引用 这时Function中的apply(T t)方法需要一个参数,那么调用的就是有一个参数的构造器
Function<Integer,Student> fun = Student::new;
//方法引用 BiFunction中的apply(T t, U u)方法需要两个参数,所以就会调用有两个参数的构造器
BiFunction<Integer,String,Student> bif = Student::new;
数组引用
- 类型::new;
//使用Function接口,传入一个长度,返回一个String数组对象
Function<Integer,String[]> fun1 = (x)-> new String[x];
String[] strs = fun1.apply(10);
System.out.println(strs.length);
//使用方法引用
Function<Integer,String[]> fun2 = String[]::new;