匿名类到Lambda表达式
我们先来看看从匿名类如何转换到Lambda表达式呢?
这里,我们可以使用两个示例来说明如何从匿名内部类转换为Lambda表达式。
-
匿名内部类到Lambda表达式
使用匿名内部类如下所示。
Runnable r = new Runnable(){ @Override public void run(){ System.out.println("Hello Lambda"); } }
转化为Lambda表达式如下所示。
Runnable r = () -> System.out.println("Hello Lambda");
-
匿名内部类作为参数传递到Lambda表达式作为参数传递
使用匿名内部类作为参数如下所示。
TreeSet<Integer> ts = new TreeSet<>(new Comparator<Integer>(){ @Override public int compare(Integer o1, Integer o2){ return Integer.compare(o1, o2); } });
使用Lambda表达式作为参数如下所示。
TreeSet<Integer> ts = new TreeSet<>( (o1, o2) -> Integer.compare(o1, o2); );
从直观上看,Lambda表达式要比常规的语法简洁的多。
Lambda表达式的语法
Lambda表达式在Java语言中引入了 “->” 操作符, “->” 操作符被称为Lambda表达式的操作符或者箭头操作符,它将Lambda表达式分为两部分:
-
左侧部分指定了Lambda表达式需要的所有参数。
Lambda表达式本质上是对接口的实现,Lambda表达式的参数列表本质上对应着接口中方法的参数列表。
-
右侧部分指定了Lambda体,即Lambda表达式要执行的功能。
Lambda体本质上就是接口方法具体实现的功能。
我们可以将Lambda表达式的语法总结如下。
1.语法格式一:无参,无返回值,Lambda体只有一条语句
Runnable r = () -> System.out.println("Hello Lambda");
具体示例如下所示。
@Test public void test1(){ Runnable r = () -> System.out.println("Hello Lambda"); new Thread(r).start(); }
2.语法格式二:Lambda表达式需要一个参数,并且无返回值
Consumer<String> func = (s) -> System.out.println(s);
具体示例如下所示。
@Test public void test2(){ Consumer<String> consumer = (x) -> System.out.println(x); consumer.accept("Hello Lambda"); }
3.语法格式三:Lambda只需要一个参数时,参数的小括号可以省略
Consumer<String> func = s -> System.out.println(s);
具体示例如下所示。
@Test public void test3(){ Consumer<String> consumer = x -> System.out.println(x); consumer.accept("Hello Lambda"); }
4.语法格式四:Lambda需要两个参数,并且有返回值
BinaryOperator<Integer> bo = (a, b) -> { System.out.println("函数式接口"); return a + b; };
具体示例如下所示。
@Test public void test4(){ Comparator<Integer> comparator = (x, y) -> { System.out.println("函数式接口"); return Integer.compare(x, y); }; }
5.语法格式五:当Lambda体只有一条语句时,return和大括号可以省略
BinaryOperator<Integer> bo = (a, b) -> a + b;
具体示例如下所示。
@Test public void test5(){ Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y); }
6.语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器能够通过上下文推断出数据类型,这就是“类型推断”
BinaryOperator<Integer> bo = (Integer a, Integer b) -> { return a + b; };
等同于
BinaryOperator<Integer> bo = (a, b) -> { return a + b; };
上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。
函数式接口
Lambda表达式需要函数式接口的支持,所以,我们有必要来说说什么是函数式接口。
只包含一个抽象方法的接口,称为函数式接口。
可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
可以在任意函数式接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
我们可以自定义函数式接口,并使用Lambda表达式来实现相应的功能。
例如,使用函数式接口和Lambda表达式实现对字符串的处理功能。
首先,我们定义一个函数式接口MyFunc,如下所示。
@FunctionalInterface public interface MyFunc <T> { public T getValue(T t); }
接下来,我们定义一个操作字符串的方法,其中参数为MyFunc接口实例和需要转换的字符串。
public String handlerString(MyFunc<String> myFunc, String str){ return myFunc.getValue(str); }
接下来,我们对自定义的函数式接口进行测试,此时我们传递的函数式接口的参数为Lambda表达式,并且将字符串转化为大写。
@Test public void test6(){ String str = handlerString((s) -> s.toUpperCase(), "binghe"); System.out.println(str); }
运行test6方法,得出的结果信息如下所示。
BINGHE
我们也可以截取字符串的某一部分,如下所示。
@Test public void test7(){ String str = handlerString((s) -> s.substring(0,4), "binghe"); System.out.println(str); }
运行test7方法,得出的结果信息如下所示。
bing
可以看到,我们可以通过handlerString(MyFunc<String> myFunc, String str)
方法结合Lambda表达式对字符串进行任意操作。
注意:作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型 。