写在前面
本文主要是简单介绍 Lambda 表达式和函数式接口的使用方法,并不涉及原理,希望初学者看完之后可以在日常开发中灵活运用 Lambda 表达式编程
Lambda 的特性
Java 8 增加了一个全新语言级别的功能,称为 Lambda 表达式。允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理存储在变量中
Lambda 表达式语法
Lambda 表达式由参数列表和方法体组成:`(type1 arg1,type2 arg2,...) -> {body;};` 参数之间用 `,` 分隔
- 1、没有入参时,用
()
空括号表示。如:() -> {body};
-
2、入参只有一个时,可以有两种表示方式:
1) (String s) -> {body}; 2)s -> {body}; 省略了参数的类型,Lambda 会根据上下文推断出参数类型,并且不需要括号包围
-
3、有多个入参时:
(String name,int age,boolean flag) -> {body}
也可以省略参数类型直接写成这样:`(name,age,flag) -> {body}` 参数列表必须有 `()` 包围
- 4、当方法体只有一条语句时,可以省略
{}。如:() -> System.out.println("hello world");
- 一些例子:1、()-> System.out.println("Lambda 表达式");
2、(String s) -> System.out.println(s);
3、s -> return s;
4、(String s,int num) -> {if(num >= 10){return s.toUpperCase();
}
return s.toLowerCase();};
5、(age,name,flag) -> 42;
Lambda 表达式不能单独使用,需要绑定函数式接口才能使用
函数式接口
函数式接口 (Functional Interface) 是 Java 8 对一类特殊类型的接口的称呼。这类接口抽象方法的数量只有一个,并且使用了 @FunctionalInterface 进行注解。需要注意的是,只有一个抽象方法的接口,即使没有 @FunctionalInterface 进行注解,编译器也会把它当作函数式接口。
特殊的是 java.util.Comparator<T> 接口具有两个抽象方法,如下:
int compare(T o1, T o2);
boolean equals(Object obj);
但它仍然是函数式接口,为什么呢?
如果接口声明了一个覆盖 java.lang.Object 公共方法的抽象方法,那么这个方法不会计入接口的抽象方法计数中,因为该接口的任何实现都将具有来自 java.lang.Object 或其他地方的针对于该方法的实现。java.util.Comparator<T> 接口中的 equals(Object obj)方法是 java.lang.Object 中的一个 public 方法的声明,不在抽象方法的计数中
实际上 Lambda 表达式就是对函数式接口中唯一的抽象方法的实现,所以 Lambda 表达式的参数需要和函数式接口抽象方法的参数列表一致。
在 jdk8 中,引入了一个新的包 java.util.function, 提供了一系列的函数式接口,这个包中的接口大致可以分为以下四类:
Function<T,R>: 接收参数,并返回结果,抽象方法为 R apply(T t)
Consumer<T>: 接收参数,无返回结果, 抽象方法为 void accept(T t)
Supplier<T>: 不接收参数,但返回结果,抽象方法为 T get()
Predicate<T>: 接收参数,返回 boolean 值,抽象方法为 boolean test(T t)
可以根据需要选择不同的函数式接口,不需要每次自定义函数式接口使用
Lambda 表达式和函数式接口的实例应用
1、获得函数式接口的实例对象,进行方法的调用
// 自定义函数式接口
@FunctionalInterface
public interface TestFunctionInterface {
// 函数式接口的唯一抽象方法,需要 Lambda 表达式实现
String testFunction(String a,int b);
}
// 测试类
public class Client {public static void main(String[] args) {
// 使用 Lambda 表达式实现 TestFunctionInterface 的抽象方法,并且得到实例对象
TestFunctionInterface testFunctionInterface = (String name,int age)->{if(age==10){return name.toUpperCase();
}
return "NO NAME!";
};
// 通过 testFunctionInterface 调用 testFunction()方法并传入参数,得到返回的结果 result
String result = testFunctionInterface.testFunction("David",10);
System.out.println(result);
}
}
打印结果:DAVID
----------
2、函数式接口作为某个方法的参数使用
// 自定义函数式接口
@FunctionalInterface
public interface TestFunctionInterface {
// 函数式接口的唯一抽象方法,需要 Lambda 表达式实现
String testFunction(String a,int b);
}
// 测试类
public class Client {public static void main(String[] args) {
// 调用 test 方法
test("steven",20,(name,age) -> {if(age==10){return name.toUpperCase();
}
return "NO NAME!";
}
);
}
// 函数式接口对象作为参数的方法
public static void test(String name,int age,TestFunctionInterface testFunctionInterface){String result = testFunctionInterface.testFunction(name,age);
System.out.println(result);
}
}
打印结果:NO NAME!
特别注意:以上两种方法使用 Lambda 表达式时,Lambda 的参数和返回值必须要和函数式接口的抽象方法保持一致
方法引用
Lambda 表达式使用 ::
来调用特定的已存在方法作为函数式接口唯一抽象方法的实现,可以使代码更具可读性
1. 静态方法
Lambda :(arg0,arg1,arg2,…) -> ClassName.staticMethod(arg0,arg1,arg2,…)
方法引用:ClassName::staticMethod
`TestStaticMethod::testStaticMethod` 即调用 `TestStaticMethod` 的静态方法 `String testStaticMethod(int age,boolean flag)` 作为函数式接口抽象方法的实现,调用的方法的参数类型和返回值需要和抽象方法保持一致
@FunctionalInterface
public interface TestFunctionInterface {String testFunction(int i);
}
public class TestStaticMethod {public static String testStaticMethod(int age,boolean flag){if(age >10 && flag){return "SUCCESS";}
return "ERROR";
}
}
public class Client {public static void main(String[] args) {//TestStaticMethod::testStaticMethod 相当于 Lambda 表达式:(int age,boolean flag) -> TestStaticMethod.testStaticMethod(age,flag); 会将 Lambda 的参数按顺序传入调用的方法中,类型不匹配会报错
TestFunctionInterface testFunctionInterface = TestStaticMethod::testStaticMethod;
String result = testFunctionInterface.testFuction(26,true);
System.out.println(result);
}
}
打印结果:SUCCESS
2. 指向任意类型的实例方法引用
Lambda:(arg0,arg1,arg2,…) -> arg0.instanceMethod(arg1,arg2,…)
方法引用:ClassName.instanceMethod(注意:arg0 是 ClassName 类型的对象)
/**
1) 函数式接口抽象方法只有一个参数
*/
@FunctionalInterface
public interface TestFunctionInterface {int testFuction(String s);
}
public class Client {public static void main(String[] args) {// 方法引用:String::length 等价于 Lambda 表达式:(arg0) -> arg0.length()
// 实际上调用参数对象 arg0 的 length()方法作为抽象方法的方法体实现,arg0.length()的返回值作为抽象方法的返回值
TestFunctionInterface testFunctionInterface = String::length;
System.out.println(testFunctionInterface.testFuction("king"));
}
}
打印结果:4
/**
2) 函数式接口抽象方法有多个参数
*/
@FunctionalInterface
public interface TestFunctionInterface {int testFuction(TestMethodRef testMethodRef, String name, int age);
}
public class TestMethodRef {public int testMethodRef(String name,int age){return name.length()+age;
}
}
public class Client {public static void main(String[] args) {//TestMethodRef::testMethodRef 等价于 (arg0,arg1,arg2) -> arg0.testMethodRef(arg1,arg2)
// 编译器只会调用第一个参数 arg0 的 testMethodRef(arg1,arg2)方法,并且把余下的 arg1,arg2 按原来的顺序作为参数传入该方法中,类型不匹配会报错
TestFunctionInterface testFunctionInterface = TestMethodRef::testMethodRef;
int result = testFunctionInterface.testFuction(new TestMethodRef(),"king",12);
System.out.println(result);
}
}
打印结果:16
3. 指向现有对象的实例方法引用
@FunctionalInterface
public interface TestFunctionInterface {char testFuction(int index);
}
public class Client {public static void main(String[] args) {
String content="hello";
//content::charAt 等价于 (int index) -> content.charAt(index);
TestFunctionInterface testFunctionInterface = content::charAt;
System.out.println(testFunctionInterface.testFuction(0));
}
}
打印结果:h
Stream API
java.util.stream.Stream 接口 和 Lambda 表达式一样,都是 Java 8 新引入的。所有 Stream 的操作必须以 Lambda 表达式为参数。Stream(流)是一个来自数据源的元素队列并支持聚合操作, 它将处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选,排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作 (terminal operation) 得到前面处理的结果,这里简单介绍一下常用的 Stream API
- forEach 提供对 stream 流元素的迭代功能
public class Client {public static void main(String[] args) {List<Info> list = new ArrayList<>();
list.add(new Info("first", 1));
list.add(new Info("second", 2));
list.add(new Info("third", 3));
list.add(new Info("four", 4));
list.add(new Info("five", 5));
//list.stream()创建 Stream 流,调用 forEach 打印所有的 name
list.stream().forEach(t->System.out.println(t.name));
}
}
打印结果:first
second
third
four
five
- filter 设置过滤限制
public class Client {public static void main(String[] args) {List<Info> list = new ArrayList<>();
list.add(new Info("first", 1));
list.add(new Info("second", 2));
list.add(new Info("third", 3));
list.add(new Info("four", 4));
list.add(new Info("five", 5));
// 针对 age 进行过滤
list.stream().filter(t->t.age>3).forEach(t->System.out.println(t.name));
}
}
打印结果:four
five
- map 将流元素映射到不同的结果
public class Client {public static void main(String[] args) {List<Info> list = new ArrayList<>();
list.add(new Info("first", 1));
list.add(new Info("second", 2));
list.add(new Info("third", 3));
list.add(new Info("four", 4));
list.add(new Info("five", 5));
// 将 Info 对象映射成 String 对象
List<String> names=list.stream().map(t->t.name).collect(Collectors.toList());
}
}
- limit 获取指定数量的流元素
Stream.of(1,2,3,4,5,6,7).limit(3).forEach(System.out::println);
打印结果:1
2
3
- skip 忽略指定数量的流元素
// 忽略前 4 个流元素
Stream.of(1,2,3,4,5,6,7).skip(4).forEach(System.out::println);
打印结果:5
6
7
- sorted 对流进行排序
public class Client {public static void main(String[] args) {List<Info> list = new ArrayList<>();
list.add(new Info("first", 1));
list.add(new Info("second", 2));
list.add(new Info("third", 3));
list.add(new Info("four", 4));
list.add(new Info("five", 5));
list.stream().sorted(new Comparator<Info>() {
@Override
// 根据 age 降序排列
public int compare(Info o1, Info o2) {return o2.age-o1.age;}
}).forEach(t->System.out.print(t.name+" "));
}
}
打印结果:five four third second first
- findFirst 返回第一个元素
//findFirst()返回 Optional 对象,需要调用 Optional.get()方法获取值
int value=Stream.of(1,2,3,4,5,6,7).findFirst().get();
System.out.println(value);
打印结果:1
- anyMatch 匹配任意一个就返回 true
//stream 流中只要有一个元素的值等于 2,就返回 true
boolean value=Stream.of(1,2,3,4,5,6,7).anyMatch(t->t==2);
System.out.println(value);
打印结果:true
- allMatch 匹配所有元素才返回 true
//stream 流中所有元素的值都等于 2,才返回 true
boolean value=Stream.of(1,2,3,4,5,6,7).allMatch(t->t==2);
System.out.println(value);
打印结果:false
- collect 将 Stream 转换为集合
List list=Stream.of(1,2,3,4,5,6,7).collect(Collectors.toList());
Set set = Stream.of(1,2,3,4,5,6,7).collect(Collectors.toSet());
最后
希望大家在开发中可以多使用 Lambda 表达式、函数式接口和 Stream API,真的很方便并且效率更高。