Lambda操作符
Lambda操作符(->)将Lambda表达式拆分成两部分:
左侧:Lambda表达式的参数列表,对应函数式接口的方法的参数列表
右侧:表达式所需执行的代码逻辑,即Lambda体,对应接口方法的实现
也就是说Lambda表达式需要函数式接口的支持,即只有一个抽象方法的接口,这种类型的接口可以使用@FunctionalInterface注解
语法格式
1、无参数,无返回值:() -> 逻辑代码
public void test() {
//匿名内部类的方式
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello world!");
}
};
r.run();
//Lambda表达式的方式
Runnable rl = () -> System.out.println("Hello Lambda!");
rl.run();
}
Runnable接口如下:
@FunctionalInterface
public interface Runnable {
void run();
}
由此可见()->System.out.println(“Hello Lambda!”);相当于对Runnable 接口中的run()方法的实现,因此我们在调用rl的run()方法时即可看到相应的效果。
注意
:JDK1.8以前在匿名内部类中如果使用了同级别的类变量,则该变量必须声明为final,JDK1.8之后则不需要显示声明为final,但是一旦变量在匿名内部类中的方法中使用则自动变成final类型,在Lambda表达式中也是如此。
public class TestLambda {
int num = 0;//无需显示声明为final,但是一旦在匿名内部类或者Lambda表达式中使用该变量则该变量会被转化为final的
@Test
public void test() {
//匿名内部类的方式
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello world!" + num);
}
};
r.run();
//Lambda表达式的方式
Runnable rl = () -> System.out.println("Hello Lambda!" + num);
rl.run();
}
}
2、有一个参数,无返回值:(x) -> 逻辑代码,此时小括号可以省略
函数式接口:
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
Lambda表达式实现和调用:
public void test1(){
Consumer<String> con = (x) -> System.out.println(x);
con.accept("Hello Lambda!");
}
public void test2(){
//省略小括号
Consumer<String> con = x -> System.out.println(x);
con.accept("Hello Lambda!");
}
3、有多个参数,有返回值,且Lambda体有多个语句:使用大括号{}将方法体括起来,并使用return返回结果
函数式接口:
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
//....
}
Lambda实现和调用:
public void test2() {
Comparator<Integer> com = (x, y) -> {
System.out.println("比较大小...");
return Integer.compare(x, y);
};
System.out.println(com.compare(1, 2));
}
当有多个参数,有返回值,但Lambda体有一条语句时,大括号和return关键字可省略:
public void test2() {
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
System.out.println(com.compare(1, 2));
}
说明
:以上通过Lambda表达式书写参数列表时我们都没有写参数的数据类型,这是因为JVM编译器可以通过上下文推断出数据类型,即类型推断。但有时JVM是无法推断出数据类型的,这时就需要我们指定数据类型,且一旦指定就需要指定全部形参的数据类型(按说这种情况是不会出现的,因为函数式接口中只会有一个抽象方法)。
示例
函数式接口声明:
@FunctionalInterface
public interface MyCal<T> {
Integer cal(T t1, T t2);
}
Lambda实现和调用:
public class TestLambda {
@Test
public void test3() {
Integer myCal = myCal(100, 200, (x, y) -> x * y);
System.out.println(myCal);
}
private Integer myCal(Integer x, Integer y, MyCal<Integer> cal) {
return cal.cal(x, y);
}
}
练习
1、对员工先根据年龄排序,年龄相同则根据姓名排序
public class LambdaExp {
List<Employee> employees = new ArrayList<Employee>(Arrays.asList(
new Employee("zhangsan", 24, 5050.00),
new Employee("lisi", 28, 6666.00),
new Employee("wangwu", 32, 3333.00),
new Employee("xueliu", 43, 2222.00),
new Employee("tianqi", 28, 7777.00),
new Employee("maer", 36, 8888.00)
));
@Test
public void test() {
Collections.sort(employees, (e1, e2) -> {
if (e1.getAge() == e2.getAge())
return e1.getName().compareTo(e2.getName());
else
return Integer.compare(e1.getAge(), e2.getAge());
});
for (Employee employee : employees) {
System.out.println(employee);
}
}
}
2、对字符串进行自定义操作
定义函数式接口
@FunctionalInterface
public interface StringHandler {
String handler(String str);
}
自定义处理字符串的方法:将接口作为方法的入参
public String strHandler(String str, StringHandler handler) {
return handler.handler(str);
}
调用时实现处理逻辑
public void test1() {
String strHandler = strHandler("abcdef", x -> x.toUpperCase());
System.out.println(strHandler);
String strHandler2 = strHandler("我爱Lambda", x -> x.substring(2));
System.out.println(strHandler2);
}
3、定义泛型接口对数据进行操作
定义函数式接口
@FunctionalInterface
public interface GenericInterface<T, R> {
R cal(T t1, T t2);
}
自定义处理泛型数据的方法
private Double myCal(Long l1, Long l2, GenericInterface<Long, Double> gi) {
return gi.cal(l1, l2);
}
实现和调用
public void test2() {
Double myCal = myCal(100L, 30L, (x, y) -> (double) (x / y));
System.out.println(myCal);
}