lambda 表达式
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。可以把Lambda表达式理解为简洁的表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表,函数主体,返回类型,可能还有一个可以抛出的异常列表。
特性
匿名:lambda表达式不像面向对象的方法一样,有确定的名称。
函数:虽然lambda不是对象的方法,属于某个特定的类。但是lambda表达式一样的有参数列表、函数主体 返回类型和异常声明
传递:lambda表达式可以作为参数传递
简洁:无需像匿名类一样有固定模板的代码,lambda写得少而想得多
JAVA8中 可以为接口增加静态方法、可以为类增加默认方法
语法
lambda 表达式的语法格式如下:
(parameters) -> expression
或
(parameters) ->{ statements; }
以下是lambda表达式的重要特征:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
1. 实战案列
1.1 入门案列
//java8之前写法
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程测试!");
}
});
thread.start();
//java8 lambda表达式 代码变的更加简洁高效
new Thread(() -> {System.out.printf("线程测试");}).start();
1.2 入门扩展
/**
* lambda 表达式扩展
*/
public class LambdaExtend {
public static void main(String[] args) {
//java8 之前匿名类写法
Calcalcuate calcalcuate = new Calcalcuate() {
@Override
public int operation(int a, int b) {
return a + b;
}
};
int c = calcalcuate.operation(1, 2);
System.out.println("c = " + c);
//java8 lambda表达式写法
//类型声明
Calcalcuate calcalcuate1 = (int a, int b) -> a + b;
int c1 = calcalcuate.operation(1, 2);
System.out.println("c1 = " + c1);
//不用类型声明
Calcalcuate calcalcuate2 = (a, b) -> a + b;
int c2 = calcalcuate2.operation(1, 2);
System.out.println("c2 = " + c2);
//类型声明和返回语句 用return的时候 记得要加大括号
Calcalcuate calcalcuate3 = (int a, int b) -> {
return a + b;
};
int c3 = calcalcuate.operation(1, 2);
System.out.println("c3 = " + c3);
//java8 lambda 表达式写法
Print print = (message) -> System.out.printf(message + "方法");
print.printMsg("测试");
//所传参数只有一个话 可以简写小括号
// Print print=message -> System.out.printf(message+"方法");
//java8 之前 表达式写法
Print print1 = new Print() {
//仔细看 lambda 表达式只相当于 参数列表(expression) 和 方法(statements)
//拆分
// (String message)-> {
// System.out.printf(message + "方法");
// }
// 合并
// Print print2 = (String message) -> {
// System.out.println(message + "方法");
// };
//print2.printMsg("测试");
//参数列表,当只有一个参数的时候,可以省略()
//expression是返回值。
//statements是代码语句,当statements只有一行的时候,可以省略{}。
@Override
public void printMsg(String message) {
System.out.println(message + "方法");
}
};
}
//计算接口 只有一个方法
interface Calcalcuate{
int operation(int a,int b);
}
//打印接口 只有一个方法
interface Print{
void printMsg(String message);
}
}
1.3 作用域
JAVA8之前 内部类只允许访问final修饰的变量,现在使用lambda表达式,一个内部类可以访问任何有效的final局部变量-任何值不会发生变化的变量
java限制了 lambda表达式访问的自由变量,值是不可更改的,因为这会导致出现无法预料的并发问题。
java编译器的限制是有限的,只对局部变量有效,如果使用静态变量,或者示例变量,编译器不会提示任何错误。这样仍然是不安全的。
可以用数组 int[] counter =new int[1]; button.setOnAaction(event->counter[0]++); 任然可以让lambda对局部变量进行重新赋值。
lambda表达式的方法体,与被嵌套的代码块具有同样的作用域,所有适用同样的命名冲突和变量屏蔽规则。
1.4 方法引用
对于已有的方法,如果希望作为lambda表达式来使用,可以直接使用方法引用
三种方法引用的情况
对象::实例方法
类::静态方法
类::实例方法
在第一种和第二种方法引用种,方法的引用等于提供方法参数的lambda表达式
例如:
System.out::println() 等同于 System.out.print(x)
Math::pow 等同于 (x,y)->Math.pow(x,y)
对于第三种,则相当于第一个参数成为执行方法的对象
例如:String::compareToIngnoreCase 等同于(x,y) x.compareIngoreCase(Y);
1.5 构造器引用
对于构造器引用,相当于根据构造器的方法的参数,生成一个构造的对象的一个lambda表达式
例如:StringBuilder::new 可以表示为 (Stiring s)->new StringBuilder(s); 具体引用哪个构造器,编译器会根据上下文推断使用符合参数的构造器。
2 函数式接口
函数式接口介绍
总结:就是只定义了一个抽象方法的接口,即使有一堆的default方法(default方法是为了增强某些API但避免现有大范围改动所有API所以推出了默认方法)
不同接口的默认方法冲突问题
如果实现的接口已有一个默认方法,但是另一个父类或者接口也有同样的默认方法。
如果是父类和接口默认方法一致,那么直接使用父类的方法实现,忽略接口中的默认方法(类优先规则,如果尝试重写默认方法toString 那么永远都不会优于Object的toString)
如果一个父接口提供了一个默认方法,另一个接口也提供了同名称和参数的方法(不论是否默认方法)那么都必须覆盖改方法。
2.1 常见函数式接口
Predicate 返回布尔值
/**
* java.util.Predicate 是一个只有test方法,返回布尔值的一个函数式接口,
* 与其类似的还有用于比较,排序的Comparator接口,其只有一个返回整数的比较接口
* @param list
* @param p
* @param <T>
* @return
*/
public static <T> List<T> filter(List<T> list, java.util.function.Predicate<T> p){
List<T> result=new ArrayList<>();
for (T t : list) {
if (p.test(t)) {
result.add(t);
}
}
return result;
}
public static void main(String[] args) {
//Predicate函数式接口示例
List<Student> studentList=new ArrayList<>();
studentList.add(new Student("李小明",17,"一班"));
studentList.add(new Student("李明",18,"一班"));
studentList.add(new Student("李大明",19,"一班"));
//java8 之前写法 执行test方法的时候 判断 是否年龄大于18
studentList=filter(studentList, new Predicate<Student>() {
//代码拆分
// test(Student p) {
// return p.getAge()>18;
// }
//合并 缩写 建议先敲匿名类方式 习惯后 在写lambda 表达式
// studentList=filter(studentList, p -> p.getAge()>18);
@Override
public boolean test(Student p) {
return p.getAge()>18;
}
});
studentList.stream().map(stl->stl.getName()).collect(Collectors.toList()).forEach(System.out::println);
}
}
Counsumer接口
Accept ()方法签名为,输入某个对象 返回void
/**
* 常用2:Consume
* consume接口定义了一个 名为accept的抽象方法,接收泛型 T 返回void
* 可用来访问T类型的对象,并且执行某些操作。
* 如下用其创建,一个foreach方法,可以实现对所有List的遍历。且对每个对象执行consume定义的操作。
* 该foreach方法,java8之后成了List接口的default方法。
* @param list
* @param <T>
*/
public static <T> void foreach(List<T> list, Consumer<T> consumer){
for (T t : list) {
consumer.accept(t);
}
}
public static void main(String[] args) {
//Predicate函数式接口示例
List<Student> studentList=new ArrayList<>();
studentList.add(new Student("李小明",17,"一班"));
studentList.add(new Student("李明",18,"一班"));
studentList.add(new Student("李大明",19,"一班"));
// 打印学生姓名
foreach(studentList, new Consumer<Student>() {
// consumer.accept(t);这一步执行的是 以下传过去的方法
//拆分
//(Student student) {
// System.out.println("student = " + student.getName());
// }
//合并 缩写
// student -> System.out.println("student = " + student.getName())
@Override
public void accept(Student student) {
System.out.println("student = " + student.getName());
}
});
}
Function
输入某个对象、返回某个对象
/**
* 常用3:function
* Apply() 方法签名:输入某个对象、返回某个对象
* @param <T>
*/
public static <T> T foreach(Function<String, T> function){
String age="20";
T result=function.apply(age);
return result;
}
public static void main(String[] args) {
Integer age= foreach(new Function<String, Integer>() {
//这里想要Integer类型 学生年龄
//拆分
//(String s) {
// return Integer.valueOf(s);
// }
//合并 简写
// foreach( s -> Integer.valueOf(s));
@Override
public Integer apply(String s) {
return Integer.valueOf(s);
}
});
System.out.println("age = " + age);
}
2.2 常见的Lambda和已有的实现
案例 | lambda例子 | 对应的函数式接口 |
布尔表达式 | boolean test(T t) | Predicate |
创建对象 | T get() | Supplier |
消费一个对象 | void accept(T t) | Consumer |
从一个对象中提取 | R apply(T t) | Function 或者其特殊化的 ToIntFunction |
合并两个值 | int applyAsInt(int left, int right) | IntBinaryOperator |
比较两个对象 | int compare(T o1, T o2) | Comparator BigFunction<Apple,Apple,Integer> ToIntBigFunction<Apple,Apple> |
参考连接