Java Lambda 表达式入门
入门
lambda表达式最简单的作用就是用于简化创建匿名内部类对象,下面先看一下什么是命令模式。
命令模式
命令模式简而言之就是将方法和方法的具体实现分离开来,只有当调用该方法时,才确定该方法的具体实现,或者说具体行为。
下面定义一个Command
接口,定义一个process
方法用来封装处理行为。
public interface Command {
//用于封装处理行为
void process(int element);
}
再定义一个数组处理类ProcessArray
,包含一个process()
方法,该方法不能确定处理数组的具体实现,所以使用了一个Command
参数,通过这个参数负责处理数组的具体实现。
public class ProcessArray {
public void process(int[] target, Command cmd){
for(var t : target){
cmd.process(t);
}
}
}
通过一个Command
接口就实现了ProcessArray
类与处理数组具体实现的分离。调用ProcessArray
类的process
方法时,处理行为是由实现Command
接口的对象决定的。下面用匿名内部类来实现处理数组的不同方法。
public class CommandTest {
public static void main(String[] args) {
var pa = new ProcessArray();
int[] target = {3, -4, 6, 4};
//第一次处理数组,这里的Command 参数,使用匿名内部类传入一个对象
pa.process(target, new Command() {
@Override
public void process(int element) {
System.out.println("数组元素的平方是:"+ element * element);
}
});
//第二次处理数组
pa.process(target, new Command() {
@Override
public void process(int element) {
System.out.println("数迭代输出数组元素:"+ element);
}
});
}
}
输出结果如下图所示:
可以看到,通过匿名内部类实现了处理数组的不同效果。
使用Lambda表达式简化创建匿名内部类对象
上面通过命令模式这个例子引出了匿名内部类的使用,而Lambda表达式可以简化匿名内部类创建对象的过程,可以使代码更简洁。
上面的代码片段:
pa.process(target, new Command() {
@Override
public void process(int element) {
System.out.println("数组元素的平方是:"+ element * element);
}
});
可以使用Lambda表达式改写为如下代码片段:
pa.process(target, (int element) -> {
System.out.println("数组元素的平方是:" + element * element);
});
可以看到Lambda表达式由三部分组成:
- 形参列表:
(int element)
可以省略形参类型,如果形参列表只有一个参数则可以省略括号也可以省略,代码片段如下:
//形参列表省略形参类型
pa.process(target, (element) -> {System.out.println("数组元素的平方是:" + element * element);});
//形参列表省略形参类型,一个参数可以省略括号
pa.process(target, element -> {System.out.println("数组元素的平方是:" + element * element);});
- 箭头:
->
- 代码块:
{}
只有一条语句可以省略花括号,代码片段如下所示:
pa.process(target, element -> System.out.println("数组元素的平方是:" + element * element));
只有一条return
语句可以省略return
,代码片段如下所示:
先定义一个Addable
接口,封装一个add
方法:
public interface Addable {
int add(int a, int b);
}
在CommandTest
类中定义一个类方法test()
,调用该方法需要一个Addable
对象:
public class CommandTest {
public static void main(String[] args) {
var pa = new ProcessArray();
int[] target = {3, -4, 6, 4};
//第一次处理数组,这里的Command 参数,使用匿名内部类传入一个对象
pa.process(target, new Command() {
@Override
public void process(int element) {
System.out.println("数组元素的平方是:"+ element * element);
}
});
//第二次处理数组
pa.process(target, new Command() {
@Override
public void process(int element) {
System.out.println("数迭代输出数组元素:"+ element);
}
});
//只有一条return语句可以省略return
test((a,b) -> a + b);
}
public static void test(Addable addable){
System.out.println("5 + 3 ="+addable.add(5,3));
}
}
总结
函数式接口
上面的代码能正常编译,说明Lambda表达式会被当成一个任意类型的对象。lambda表达式的类型被称作目标类型,其目标类型必须为函数式接口(functional interface)(只包含一个抽象方法的接口)。因为Lambda表达式的结果就是被当成对象,所以可以用来赋值。
@FunctionalInterface
public interface Runnable {
void run();
}
Runnable
是java本身的函数式接口,下面使用Lambda表达式创建一个Runnable对象
Runnable r = () -> {
for (var i = 0; i < 100; i++){
System.out.println();
}
};
总结
在Lambda表达式中使用var
var的类型是由编译器推断的,所以使用Lambda表达式给var赋值时需要指明Lambda表达式的类型。
//要进行强制转换指明类型
var run = (Runnable)()->{
for (var i = 0; i < 100; i++){
System.out.println();
}
};
总结
方法引用与构造器引用
Lambda表达式的代码块如果只有一条代码,则在代码块中可以使用方法引用和构造器引用。两种引用都需要使用两个英文冒号“::
”。
先定义一个函数式接口Converter
@FunctionalInterface
interface Converter{
Integer convert(String from);
}
再使用Lambda表达式创建Converter
对象,通过该对象调用convert
方法
public class MethodRefer {
public static void main(String[] args) {
Converter converter1 = from -> Integer.valueOf(from);
var val = converter1.convert("99");
System.out.println(val);
}
}
由于converter1
是由Lambda表达式创建的,所以convert()方法的执行体是Lambda表达式的代码块部分。
上述代码块可以改写为方法引用:
//函数式接口中抽象方法的全部参数传给该类方法作为参数
Converter converter1 = Integer::valueOf;
使用Lambda表达式创建Converter2
对象
Converter converter2 = from -> "abc".indexOf(from);
var val1 = converter2.convert("c");
输出2
上述代码可以使用方法引用替换:
Converter converter2 = "abc"::indexOf;
var val1 = converter2.convert("c");
先定义一个函数式接口Mytest
interface MyTest {
String test(String a, int b, int c);
}
使用Lambda表达式创建Mytest对象,通过该对象调用test
方法
MyTest mt = (a, b, c) -> a.substring(b, c);
var str = mt.test("123",1,2);
System.out.println(str);
上述代码可以使用方法引用替换:
MyTest mt = String::substring;
var str = mt.test("123",1,2);
先定义一个函数式接口YourTest
interface YouTest {
JFrame win(String title);
}
使用Lambda表达式创建YourTest对象,通过该对象调用win方法
YouTest yt = a -> new JFrame(a);
var jf = yt.win("window");
System.out.println(jf);
上述代码可以使用构造器引用替换:
//函数式接口中抽象方法的全部参数传给该构造器作为参数
YouTest yt = JFrame::new;
var jf = yt.win("window");
System.out.println(jf);