Java-Lambda表达式学习笔记
在IntelliJ IDEA中使用Comparator匿名内部类的时候,编译器会推荐你使用Lambda表达式。原因就是我们真正想要用的是Comparator里面的compare方法,并不想创建一个Comparator的实现类对象之后,再来调用里面的方法,因为这样太麻烦,代码不够简洁。(注意,实际上,Lambda表达式的底层还是创建了对象)。
1.函数式接口
Lambda表达式的使用必须依赖函数式接口。所谓函数式接口指的是该接口有且只有一个没有提供默认实现的且不是重写Object方法的抽象方法。
public interface Demo{
void method();
//提供默认实现的方法自行忽略即可,函数式接口要求接口有且仅有一个没有提供默认实现的抽象方法
default void method1(){}
// ..........
default void methodN(){}
//与Object方法中所有可重写的方法一模一样的也不算,如下面的equals
boolean equals(Object o);
}
比如Comparator里面就有一个 boolean equals(Object o)
的抽象方法和一个int compare(T o1, T o2)
的抽象方法,但是它仍然被当做是函数式接口。
自定义函数式接口的时候可以在接口上加一个@FunctionalInterface
注解,目的是告诉编译器这个接口是一个函数式接口,写错了,你给我管管。有点像@Override
.
案例:
Worker
package com.lordbao.entity;
/**
* @Author Lord_Bao
* @Date 2020/9/14 20:25
* @Version 1.0
*/
public class Worker {
private String name;
private int age;
private int salary;
public Worker() {
}
public Worker(String name, int age, int salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Worker{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
Test
package com.lordbao.demo1;
import com.lordbao.entity.Worker;
import java.util.ArrayList;
import java.util.List;
/**
* @Author Lord_Bao
* @Date 2020/9/14 20:25
* @Version 1.0
*/
public class TestComparator {
public static void main(String[] args) {
List<Worker> list = new ArrayList<>();
Worker worker1 = new Worker("Adam",25,10000);
Worker worker2 = new Worker("Jack",24,9000) ;
Worker worker3 = new Worker("Tom",23,9000);
Worker worker4 = new Worker("Leo",23,9000);
list.add(worker1);
list.add(worker2);
list.add(worker3);
list.add(worker4);
//使用Lambda表达式
//先按照工资降序
//再按照年龄降序
//最后照姓名升序
list.sort((o1,o2)->{
if (o1.getSalary() == o2.getSalary()){
if (o1.getAge() == o2.getAge()){
//按照名字升序
return o1.getName().compareTo(o2.getName());
}
else {
//按照年龄降序
return o2.getAge()-o1.getAge();
}
}else {
//按照工资降序
return o2.getSalary() - o1.getSalary();
}
});
for (Worker worker : list) {
System.out.println(worker);
}
}
}
结果
Worker{name=‘Adam’, age=25, salary=10000}
Worker{name=‘Jack’, age=24, salary=9000}
Worker{name=‘Leo’, age=23, salary=9000}
Worker{name=‘Tom’, age=23, salary=9000}
2.基本语法
总的来说,Lambda表达式的语法结构为
参数列表 -> 方法体
根据方法的参数是否有个数和方法是否有返回值,可以将语法分为四个部分,即
有参数 | 无参数 | |
---|---|---|
有返回值 | 1.当参数仅有一个,参数无需写小括号, 2.多参数必须写小括号。 3.不必写参数类型,因为类型可推断。 4.方法体只有一个返回语句时, 可以省略花括号和return一起不写 | 1.参数列表必须写小括号 2.方法体只有一个返回语句时, 可以省略花括号和return一起不写 |
无返回值 | 1.当参数仅有一个,参数无需写小括号, 2.多参数必须写小括号。 3.不必写参数类型,因为类型可推断。 | 参数列表必须写小括号 |
注意,当方法体仅有一句代码时,无需写花括号。
案例(Lambda表达式的两个用法)
package com.lordbao.testSyntax;
import java.util.Comparator;
/**
* @Author Lord_Bao
* @Date 2020/9/26 20:09
* @Version 1.0
*/
public class Main1 {
public static void main(String[] args) {
//用法1 声明一个函数式接口引用变量,并指向由Lambda式结构而产生的对象
//注意 这里和匿名内部类的本质是一样的,还是通过new创建一个匿名内部类对象
Runnable myRunnable = ()-> System.out.println("hello world");
//用法2 其实和用法1差不多
Thread myThread = new Thread(()-> System.out.println("hello java"));
}
}
3.Java内置的常见的4个函数式接口
Consumer<T> : void accept(T t);
Supplier<T> : T get();
Function<T, R> :R apply(T t);
Predicate<T> :boolean test(T t);
上面为内置的4个函数式接口,当然内置的函数式接口并不止四个,学会核心的就可以了。具体的案例如下:
Test
package com.lordbao.testInternalInterface;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* @Author Lord_Bao
* @Date 2020/9/26 20:26
* @Version 1.0
*/
public class Test {
public static void main(String[] args) {
consumeString((rawString) -> System.out.println(rawString),"I love you");
Integer loveNumber = getInt(() -> 520);
System.out.println(loveNumber);
String englishLoveWords = getStringAfterProcessing((rawString)->{
// 这里模拟加工 ....
//
return "I love you";
},"我爱你");
System.out.println(englishLoveWords);
boolean youReallyLoveHer = predicate((lawString)-> lawString.toUpperCase().contains("I LOVE YOU"),"I love you so much");
System.out.println(youReallyLoveHer);
}
// 测试Consumer<T> 接口 , 方法:void accept(T t)
public static void consumeString(Consumer<String> consumer,String rawString){
consumer.accept(rawString);
}
//测试Supplier<T> 接口 , 方法:T get()
public static Integer getInt(Supplier<Integer> supplier){
return supplier.get();
}
//测试Function<T,R> 接口 , 方法:R apply(T t)
public static String getStringAfterProcessing(Function<String,String> function,String rawString){
return function.apply(rawString);
}
//测试Predicate<T> 接口 , 方法:boolean test(T t)
public static boolean predicate(Predicate<String> predicate,String rawString){
return predicate.test(rawString);
}
}
结果
I love you
520
I love you
true
4.总结
Lambda表达式依赖于函数式接口,那么函数式接口的概念就要掌握。
Lambda表达式的基本语法是什么,根据函数式接口的方法有无返回值,参数的个数来记忆。
Lambda表达式怎么去理解?将它当做函数式接口的一个实现类对象。
Lambda表达式干什么用?为了简化代码。比如,我只需要某个函数式接口的方法,并不需要这个函数式接口的对象,那么就可以用Lambda表达式。但是,需要注意两点:
- Lambda表达式仍然是创建了函数式接口的实现类对象,这一点和匿名内部类很像。
- Lambda表达式的本质是为了简化代码,如果影响可读性那就得不偿失,比如带有返回值的方法在仅有一行代码的情况下还是不要省掉return,否则就会让其他人很迷惑。技术是为了实用,而不是为了炫技。