一.概念
Lambda表达式是特殊的匿名内部类,语法更简洁
Lambda表达式允许把函数作为一个方法的参数(函数作为方法参数传递),将代码像数据一样传递
二.语法
基本语法
<函数式接口><变量名>=(参数1,参数2....)->{方法体}
(parameters) -> expression 或 (parameters) ->{ statements; }
函数式接口:接口中只有一个抽象方法
1.paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。
2.->:可理解为“被用于”的意思
3.方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回。
4.如果想用lambda表达式必须是函数式接口
// 1. 不需要参数,返回值为 2
() -> 2
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的和
(x, y) -> x + y
// 4. 接收2个int型整数,返回他们的乘积
(int x, int y) -> x * y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
三.无参无返回值lambda表达式
//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {
void test();
}
//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {
void test(int a);
}
//无返回值两个参数
@FunctionalInterface
interface MoreParameterNoReturn {
void test(int a,int b);
}
public class TestDemo {
public static void main(String[] args) {
NoParameterNoReturn n = ()->{
System.out.println("无参数无返回值");
};
n.test();
OneParameterNoReturn o = (a)-> {
System.out.println("无返回值一个参数"+a);
};
o.test(666);
MoreParameterNoReturn m = (int a,int b)->{
System.out.println("无返回值两个参数"+a+" "+b);
};
m.test(666,999);
}
}
四.有返回值函数接口
//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {
int test();
}
//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
int test(int a);
}
//有返回值多个参数
@FunctionalInterface
interface MoreParameterReturn {
int test(int a,int b);
}
public class TestDemo {
public static void main(String[] args) {
NoParameterReturn n = ()->{
return 666;
};
int ret1 = n.test();
System.out.println(ret1);
System.out.println("================");
OneParameterReturn o = (int a)->{
return a;
};
int ret2 = o.test(999);
System.out.println(ret2);
System.out.println("================");
MoreParameterReturn m = (int a,int b)-> {
return a+b;
};
int ret3 = m.test(10,90);
System.out.println(ret3);
}
}
五.语法精简
- 参数类型可以省略,如果需要省略,每个参数的类型都要省略。
- 参数的小括号里面只有一个参数,那么小括号可以省略
- 如果方法体当中只有一句代码,那么大括号可以省略
- 如果方法体中只有一条语句,其是return语句,那么大括号可以省略,且去掉return关键字
public static void main(String[] args) { MoreParameterNoReturn moreParameterNoReturn = (a, b)->{ System.out.println("无返回值多个参数,省略参数类型:"+a+" "+b); }; moreParameterNoReturn.test(20,30); OneParameterNoReturn oneParameterNoReturn = a ->{ System.out.println("无参数一个返回值,小括号可以省略:"+ a); }; oneParameterNoReturn.test(10); NoParameterNoReturn noParameterNoReturn = ()->System.out.println("无参数无返回值,方法体中只有 一行代码"); noParameterNoReturn.test(); //方法体中只有一条语句,且是return语句 NoParameterReturn noParameterReturn = ()-> 40; int ret = noParameterReturn.test(); System.out.println(ret); }
六.函数式接
要了解Lambda表达式,首先需要了解什么是函数式接口,函数式接口定义:一个接口有且只有一个抽象方法
1.如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口,可以使用Lambda表达式,Lambda表达式会被匹配到这个抽象方法上
2.如果我们在某个接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的。
3.Lambda表达式只能简化函数式接口的匿名内部类的写法
定义方式:
@FunctionalInterface interface NoParameterNoReturn { void test(); default void test2() { System.out.println("JDK1.8新特性,default默认方法可以有具体的实现"); } }
1.常见的函数式接口
一、匿名内部类,new函数式接口
public class Demo01 {
//匿名内部类的方式,new接口,里面重写接口中的方法
Function<String,Object> function = new Function<String,Object>() {
@Override
public Object apply(String str) {
return str;
}
};
}
二、Lambda表达式替换函数式接口
public class Demo01 {
public static void main(String[] args) {
//Lambda表达式简化匿名内部类
Function<String,Object> function = (str)->{return str;};
System.out.println(function.apply("张三"));
}
}
三.消费型接口 Consumer<T>
@Test
public void test1(){
happyTime(500, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println(aDouble);
}
});
happyTime(500,money -> System.out.println(money));
}
public void happyTime(double money, Consumer<Double> con){
con.accept(money);
}
四. 函数型接口 Function<T, R>
@Test
public void test3(){
String str1 = integerToString(new Integer(3), new Function<Integer, String>() {
@Override
public String apply(Integer integer) {
return integer.toString();
}
});
System.out.println(str1);
String str2 = integerToString(new Integer(4), integer -> integer.toString());
System.out.println(str2);
}
public String integerToString(Integer integer, Function<Integer,String> fun){
return fun.apply(integer);
}
五.断定型接口 Predicate<T>
@Test
public void test4(){
List<String> list= Arrays.asList("北京","南京","上海");
List<String> filterStrs=filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
System.out.println(filterStrs);
List<String> filterStrs1=filterString(list,s -> s.contains("京"));
System.out.println(filterStrs1);
}
//根据给定的规则,过滤集合中的字符串,此规则由Predicate的方法决定
public List<String> filterString(List<String> list, Predicate<String> pre){
ArrayList<String> filterList = new ArrayList<>();
for(String s : list){
if(pre.test(s)){
filterList.add(s);
}
}
return filterList;
}
七.方法引用
1.语法
1.静态方法引用(Static Method Reference):使用类名和双冒号(::)来引用一个静态方法。例如,Math::max
表示引用Math类中的静态max方法
类名::静态方法名称
2.实例方法引用(Instance Method Reference):使用实例对象和双冒号(::)来引用一个非静态的实例方法。例如,String::length
表示引用String对象的length方法。
实例对象::方法名称
3.构造方法引用(Constructor Method Reference):使用类名和关键字new
来引用一个构造方法。例如,ArrayList::new
表示引用ArrayList类的构造方法。
类名::new
4.引用特定类型的任意对象的实例方法(Reference to an Instance Method of a Particular Object):使用特定对象的实例和双冒号(::)来引用该对象的实例方法。
例如,myObject::methodName
表示引用myObject对象的methodName方法。
特定类实例对象::方法名称
2.案例
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
class Vehicle {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
// 静态方法
public static void printName(String name) {
System.out.println(name);
}
// 实例方法
public void printPowerValue(Long powerValue) {
System.out.println(powerValue);
}
@Override
public String toString() {
return "Vehicle{" +
"type='" + type + '\'' +
'}';
}
}
public class MethodReferenceExample {
public static void main(String[] args) {
// 静态方法引用: 类名::静态方法
List<String> names = Arrays.asList("擎天柱", "霸天虎", "威震天");
names.forEach(Vehicle::printName);
System.out.println();
// 实例方法引用: 对象名::实例方法
Vehicle example = new Vehicle();
List<Long> messages = Arrays.asList(50000L, 120000L);
messages.forEach(example::printPowerValue);
System.out.println();
// 构造方法引用: 类名::new
Supplier<Vehicle> vehicleSupplier = Vehicle::new;
// 创建对象
Vehicle car = vehicleSupplier.get();
car.setType("Car");
Vehicle bike = vehicleSupplier.get();
bike.setType("Bike");
// 打印对象
System.out.println(car);
System.out.println(bike);
System.out.println();
// 引用特定类型的任意对象的实例方法: 对象名::实例方法
String prefix = "Prefix:";
List<String> suffixes = Arrays.asList("A", "B", "C");
suffixes.stream()
.map(prefix::concat) // 使用String的concat方法
.forEach(System.out::println); // 使用System.out的println方法
}
}
3.注意事项
1.方法引用只能用于函数式接口:方法引用只能用于那些只有一个抽象方法的接口,也就是函数接口。确保在使用方法引用时,目标类型是一个函数接口,否则编译将会失败。
2.参数匹配:方法引用的参数必须与目标方法的参数列表兼容。这意味着参数数量和类型要一致。如果目标方法具有多个参数,可以通过上下文推断或者函数式接口的泛型来进行类型匹配。
3.方法引用可能引发空指针异常:在使用实例方法引用时,请确保对象引用不是null。否则,在调用实例方法时会引发空指针异常。因此,在进行实例方法引用操作前,请先对对象进行必要的空值检查。
4.正确使用方法引用,增加代码可读性:正确使用方法引用可以使代码更加简洁、清晰和易于理解。然而,过度使用方法引用可能会导致代码难以阅读和维护。请在合适的情况下使用方法引用,并在需要更明确的表达时使用传统的Lambda表达式(或匿名类)来代替。
5.选择最适合的方法引用方式:熟悉各种方法引用的不同形式,包括静态方法引用、实例方法引用、构造方法引用和引用特定类型的任意对象的实例方法。了解这些不同的使用形式,可以根据具体的编码需求选择最适合的方法引用方式。
6.方法引用与Lambda表达式的比较:在某些情况下,方法引用可以代替Lambda表达式来提高代码可读性。然而,并非所有情况都适合使用方法引用。有时,使用Lambda表达式可能更清晰明了。