一、Lambda表达式的介绍
Lambda表达式是Java8中最重要的新功能之一。使用Lambda表达式可以替代只有一个抽象函数的接口实现,告别匿名内部类,代码看起来更简洁易懂。Lambda表达式同时还提升了对集合、框架的迭代、遍历、过滤数据的操作。
Lambda表达式的特点
- 函数式编程。
- 参数类型自动推断。
- 代码量少,简洁。
二、Lambda表达式的使用
- 案例一:
public class LambdaDemo {
public static void main(String[] args) {
// 匿名内部类方式
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("running1.....");
}
});
thread.start();
// Lambda表达式方式
new Thread(()->{System.out.println("running2.....");}).start();
}
}
/*
输出的结果是:
running1.....
running2.....
*/
- 案例二:
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class LambdaDemo {
public static void main(String[] args) {
List<String> list = Arrays.asList("java","javasccript","scala","python");
// 普通匿名内部类方式
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
});
for (String str:list){
System.out.println(str);
}
System.out.println("------------------");
// Lambda表达式方式
Collections.sort(list,(a,b)->a.length()-b.length());
list.forEach(System.out::println);
}
}
/*
输出的结果是:
java
scala
python
javasccript
------------------
java
scala
python
javasccript
*/
为什么要使用Lambda表达式
先来看一个简单的案例:
学生类:
public class Student {
private String name;
private int age;
private int score;
public Student() {
}
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
测试类:
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<Student>();
list.add(new Student("zhangsan",14,67));
list.add(new Student("lisi",13,89));
list.add(new Student("wangwu",15,97));
list.add(new Student("maliu",12,63));
list.add(new Student("zhaoqi",16,75));
//查找年龄大于14的学生
findByAge(list);
System.out.println("--------------");
//查找分数大于75的学生
findByScore(list);
}
public static void findByAge(ArrayList<Student> students){
ArrayList<Student> list = new ArrayList<Student>();
for (Student student:students){
if (student.getAge() > 14){
list.add(student);
}
}
for (Student student:list){
System.out.println(student);
}
}
public static void findByScore(ArrayList<Student> students){
ArrayList<Student> list = new ArrayList<Student>();
for (Student student:students){
if (student.getScore() > 75){
list.add(student);
}
}
for (Student student:list){
System.out.println(student);
}
}
}
/*
输出的结果是:
Student{name='wangwu', age=15, score=97}
Student{name='zhaoqi', age=16, score=75}
--------------
Student{name='lisi', age=13, score=89}
Student{name='wangwu', age=15, score=97}
*/
我们通过观察上述案例发现虽然在代码逻辑上没有太大的问题,但是我们可以发现测试类中有出现代码重复的情况,不是那么的完美;这时我们可以通过Lambda表达式来进行代码优化。
学生类:
public class Student {
private String name;
private int age;
private int score;
public Student() {
}
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
定义一个接口:
import com.liyunfei.Student;
public interface StudentFilter {
boolean compare(Student student);
}
测试类:
import com.liyunfei.Student;
import com.liyunfei.StudentFilter;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<Student>();
list.add(new Student("zhangsan",14,67));
list.add(new Student("lisi",13,89));
list.add(new Student("wangwu",15,97));
list.add(new Student("maliu",12,63));
list.add(new Student("zhaoqi",16,75));
getByFilter(list,(e)->e.getAge() > 14 );
System.out.println("---------------------");
getByFilter(list,(e)->e.getScore() > 75 );
System.out.println("---------------------");
getByFilter(list,(e)->e.getName().length() > 5 );
}
public static void getByFilter(ArrayList<Student> students, StudentFilter filter){
ArrayList<Student> list = new ArrayList<Student>();
for (Student student:students){
if (filter.compare(student)){
list.add(student);
}
}
printStudent(list);
}
public static void printStudent(ArrayList<Student> students){
for (Student student:students){
System.out.println(student);
}
}
}
/*
输出的结果是:
Student{name='wangwu', age=15, score=97}
Student{name='zhaoqi', age=16, score=75}
---------------------
Student{name='lisi', age=13, score=89}
Student{name='wangwu', age=15, score=97}
---------------------
Student{name='zhangsan', age=14, score=67}
Student{name='wangwu', age=15, score=97}
Student{name='zhaoqi', age=16, score=75}
*/
三、Lambda表达式的应用场景
任何有函数式接口的地方。
函数式接口
只有一个抽象方法(Object类中的方法除外)的接口是函数式接口。
接口 | 描述 |
---|---|
Supplier | 代表一个输出 |
Consumer | 代表一个输入 |
BiConsumer | 代表两个输入 |
Function | 代表一个输入,一个输出(一般输入和输出是不同类型的) |
UnaryOperator | 代表一个输入,一个输出(输入和输出是相同类型的) |
BiFunction | 代表两个输入,一个输出(一般输入和输出是不同类型的) |
BinaryOperator | 代表两个输入,一个输出(输入和输出是相同类型的) |
Supplier:
import java.util.function.Supplier;
public class lambdaTest {
public static void main(String[] args){
Supplier<String> s1 = ()->{return "liyunfei";};
Supplier<String> s2 = ()->"liyunfei2";
System.out.println(s1.get());
System.out.println(s2.get());
}
}
/*
输出的结果是:
liyunfei
liyunfei2
*/
Consumer:
import java.util.function.Consumer;
public class lambdaTest {
public static void main(String[] args) {
Consumer<String> c11 = (str) -> System.out.println(str);
c11.accept("anhui");
}
}
// 输出的结果是:anhui
BiConsumer:
import java.util.function.BiConsumer;
public class lambdaTest {
public static void main(String[] args) throws Exception {
BiConsumer<String,String> bc = (str1, str2)-> System.out.println(str1.length() + str2.length());
bc.accept("zhangsan","lisi");
}
}
// 输出的结果是:12
Function:
import java.util.function.Function;
public class lambdaTest {
public static void main(String[] args) {
Function<String,Integer> f1 = (str)->{return str.length();};
System.out.println(f1.apply("abcdefg"));
}
}
// 输出的结果是:7
UnaryOperator:
import java.util.function.UnaryOperator;
public class lambdaTest {
public static void main(String[] args) throws Exception {
UnaryOperator<String> unaryOperator = (str)-> {return str;};
System.out.println(unaryOperator.apply("zhangsan"));
}
}
// 输出的结果是:zhangsan
BiFunction:
import java.util.function.BiFunction;
public class lambdaTest {
public static void main(String[] args) {
BiFunction<String, String, Integer> bf = (a, b) -> a.length() + b.length();
System.out.println(bf.apply("飞哥", "好帅"));
}
}
// 输出的结果是:4
BinaryOperator:
import java.util.function.BinaryOperator;
public class lambdaTest {
public static void main(String[] args) throws Exception {
BinaryOperator<String> unaryOperator = (a,b)-> {return a + b;};
System.out.println(unaryOperator.apply("飞哥","好帅"));
}
}
// 输出的结果是:飞哥好帅
方法的引用
方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法,方法引用提供了一种引用而不执行方法的方式,如果抽象方法的实现恰好可以使用调用另外一个方法来实现,就有可能可以使用方法引用。
- 方法引用的分类:
类型 | 语法 | 对应的Lambda表达式 |
---|---|---|
静态方法引用 | 类名::staticMethod | (args)->类名.staticMethod(args) |
实例方法引用 | inst::instMethod | (args)->inst.instMethod(args) |
对象方法引用 | 类名::instMethod | (args)->类名.instMethod(args) |
构造方法引用 | 类名::new | (args)->new 类名(args) |
- 静态方法的引用:如果函数式接口的实现恰好可以通过调用一个静态方法来实现,那么就可以使用静态方法引用。
演示案例:
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class Test {
static String put(){
System.out.println("put......");
return "put";
}
public static void getSize(int size){
System.out.println(size);
}
public static String toUpperCase(String str){
return str.toUpperCase();
}
public static Integer getLength(String str,String str2){
return str.length() + str2.length();
}
public static void main(String[] args) {
Supplier<String> s2 = Test::put;
System.out.println(s2.get());
Supplier<String> s3 = Fun::hehe;
System.out.println(s3.get());
Consumer<Integer> c1 = Test::getSize;
c1.accept(123);
Function<String,String> f1 = Test::toUpperCase;
Function<String,String> f2 = Fun::toUpperCase;
System.out.println(f1.apply("abc"));
System.out.println(f2.apply("abc"));
BiFunction<String,String,Integer> bf = Test::getLength;
System.out.println(bf.apply("飞哥","好帅"));
}
}
class Fun{
public static String hehe(){
return "hehe";
}
public static String toUpperCase(String str){
return str.toUpperCase();
}
}
/*
输出的结果是:
put......
put
hehe
123
ABC
ABC
4
*/
- 实例方法的引用:如果函数式接口的实现恰好可以通过调用一个实例的实例方法来实现,那么就可以使用实例方法引用。
演示案例:
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class Test {
public String put(){
return "put...";
}
public void getSize(int size){
System.out.println("size:" + size);
}
public String toUpperCase(String str){
return str.toUpperCase();
}
public static void main(String[] args) {
Supplier<String> s1 = new Test()::put;
System.out.println(s1.get());
//唯一的创建一个test3对象
Test3 test = new Test();
Consumer<Integer> c1 = test::getSize;
c1.accept(123);
Function<String,String> f1 = test::toUpperCase;
System.out.println(f1.apply("abc"));
}
}
/*
输出的结果是:
put...
size:123
ABC
*/
- 对象方法引用:抽象方法的第一个参数类型刚好是实例方法的类型,抽象方法剩余的参数恰好可以当做实例方法的参数。如果函数式接口的实现能由上面的说的实例方法调用来实现的话,那么就可以使用对象方法引用。
演示案例:
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
public class Test {
public static void main(String[] args) {
Consumer<Too> c3 = Too::foo;
c3.accept(new Too());
BiConsumer<Too2,String> bc = Too2::show;
bc.accept(new Too2(),"abc");
BiFunction<Exec,String,Integer> bf2 = Exec::test;
System.out.println(bf2.apply(new Exec(), "def"));
}
}
class Exec{
public int test(String name){
return 1;
}
}
class Too{
public Integer fun(String s){
return 1;
}
public void foo(){
System.out.println("foo");
}
}
class Too2{
public Integer fun(String s){
return 1;
}
public void foo(){
System.out.println("foo-----too2");
}
public void show(String str){
System.out.println("show-----too2" + str);
}
}
/*
输出的结果是:
foo
show-----too2abc
1
*/
- 构造方法引用:如果函数式接口的实现恰好可以通过调用一个类的构造方法来实现,那么就可以使用构造方法引用。
演示案例:
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class Test {
public static void main(String[] args) {
Supplier<Person> s2 = Person::new;
s2.get();
Supplier<List> s3 = ArrayList::new;
System.out.println(s3);
Supplier<Set> s4 = HashSet::new;
System.out.println(s4);
Supplier<Thread> s5 = Thread::new;
System.out.println(s5);
Supplier<String> s6 = String::new;
System.out.println(s6);
Consumer<Integer> c1 = Account::new;
c1.accept(123);
Function<String,Account> f1 = Account::new;
f1.apply("abc");
}
}
class Account{
public Account(){
System.out.println("调用无参构造方法");
}
public Account(int age){
System.out.println("age 参数构造" + age);
}
public Account(String str){
System.out.println("str 参数构造" + str);
}
}
class Person{
public Person(){
System.out.println("调用无参的构造方法");
}
}
/*
输出的结果是;
调用无参的构造方法
com.liyunfei.Test5$$Lambda$15/0x000001e4a4c03600@1d81eb93
com.liyunfei.Test5$$Lambda$16/0x000001e4a4c03810@34a245ab
com.liyunfei.Test5$$Lambda$17/0x000001e4a4c03a20@12edcd21
com.liyunfei.Test5$$Lambda$18/0x000001e4a4c03c30@52cc8049
age 参数构造123
str 参数构造abc
*/