JAVA Lambda 表达式

简介: JAVA8引入Lambda就是为了简化代码,允许把函数作为一个方法的参数传递进方法中。它是一个匿名函数,是一种没有声明的方法,即没有访问修饰符,返回值声明和名称。在仅使用一次方法的地方特别有用,方法定义很短。它为我们节省了,如包含类声明和编写单独方法的工作。

一、 Lambda简介

简单的来说,java8引入Lambda的目的是为了简化代码,允许把函数作为一个方法的参数传递进方法中。如果有JavaScript的编程经验,马上会想到这不就是闭包吗。是的,Lambda表达式也可以称作Java中的闭包。

Java8以前,如果想把某个接口的实现类作为参数传递给一个方法会怎么做?要么创建一个类实现该接口,然后new出一个对象,在调用方法时传递进去,要么使用匿名类,可以精简一些代码。以创建一个线程并打印一行日志为例,使用匿名函数写法如下:

newThread(newRunnable() { @Overridepublicvoidrun() { System.out.println("开启一个线程执行任务"); }
}).start();

再来看看使用Lambda表达式,上面的代码会变成什么样子。

newThread(() ->System.out.println("开启一个线程执行任务")).start();

Java 中的 Lambda 表达式通常使用语法是 (argument) -> (body),比如:

(arg1, arg2...) -> { body }
(type1arg1, type2arg2...) -> { body }

二、lambda表达式的基本使用

1、基本语法

在上面的例子中我们使用了这样一行() -> System.out.println("使用Lambda表达式");下面我们对lambda的格式进行一个介绍:

(1)左边括号:lambda的形参列表,就好比是我们定义一个接口,里面有一个抽象方法,这个抽象方法的形参列表。

(2)箭头:lambda的操作符,所以你看见这个箭头心中知道这是一个lambda表达式就可以了。

(3)右边lambda体:就好比是我们实现了接口中的抽象方法。

2、基本特点

  • Lambda 表达式可以具有零个,一个或多个参数。
  • 可以显式声明参数的类型,也可以由编译器自动从上下文推断参数的类型。例如 (int a) 与刚才相同 (a)
  • 参数用小括号括起来,用逗号分隔。例如 (a, b)(int a, int b)(String a, int b, float c)
  • 空括号用于表示一组空的参数。例如 () -> 42
  • 当有且仅有一个参数时,如果不显式指明类型,则不必使用小括号。例如 a -> return a*a
  • Lambda 表达式的正文可以包含零条,一条或多条语句。
  • 如果 Lambda 表达式的正文只有一条语句,则大括号可不用写,且表达式的返回值类型要与匿名函数的返回类型相同。
  • 如果 Lambda 表达式的正文有一条以上的语句必须包含在大括号(代码块)中,且表达式的返回值类型要与匿名函数的返回类型相同。

3、基本使用

3.1 无参无返回值

//这是一种最简单的情况//此时如果方法体比较复杂好几行代码,那么这个{}是不能省略的Runnablerunnable1= () -> {
System.out.println("第一行代码");
System.out.println("第二行代码");
};

3.2 有参数无返回值

@TestpublicvoidConsumerTest() {
//第一种:没有使用lambda表达式Consumer<String>consumer=newConsumer<String>() {
@Overridepublicvoidaccept(Strings) {
System.out.println(s);
            }
        };
consumer.accept("hello,world");
//第二种:使用lambda表达式Consumer<String>consumer1= (Strings)->//此时只有一行输出代码,因此可以省去外部的{}System.out.println(s);
consumer.accept("hello,world");
    }

3.3 有参数有返回值

@Testpublicvoidtest() {
//第一种:没有使用lambda表达式Comparator<Integer>comparator=newComparator<Integer>() {
@Overridepublicintcompare(Integero1, Integero2) {
returno1.compareTo(o2);
            }
        };
System.out.println(comparator.compare(1,2));
System.out.println("======================");
//第二种:使用lambda表达式Comparator<Integer>comparator2= (o1,o2)->o1.compareTo(o2);
System.out.println(comparator2.compare(1,2));
    }

三、FunctionalInterface介绍

在 Java 中,功能接口(Functional interface)指只有一个抽象方法的接口比如我们的Runnable就是一个函数式接口,我们可以到源码中看看:

@FunctionalInterfacepublicinterfaceRunnable {
/*** When an object implementing interface <code>Runnable</code> is used* to create a thread, starting the thread causes the object's* <code>run</code> method to be called in that separately executing* thread.* <p>* The general contract of the method <code>run</code> is that it may* take any action whatsoever.** @see     java.lang.Thread#run()*/publicabstractvoidrun();
}


1、函数式接口特点

(1)含有@FunctionalInterface注解

(2)只有一个抽象方法

2、函数式接口作用

函数式接口能够接受匿名内部类的实例化对象,换句话说,我们可以使用匿名内部类来实例化函数式接口的对象,而Lambda表达式能够代替内部类实现代码的进一步简化。并且java为我们提供了四个比较重要的函数式接口:

  1. 消费型接口:Consumer< T> void accept(T t)有参数,无返回值的抽象方法;
  2. 供给型接口:Supplier < T> T get() 无参有返回值的抽象方法;
  3. 断定型接口: Predicate< T> boolean test(T t):有参,但是返回值类型是固定的boolean
  4. 函数型接口: Function< T,R> R apply(T t)有参有返回值的抽象方法;

3、自定义函数式接口

@FunctionalInterfacepublicinterfaceWorkerInterface {
publicvoiddoSomeWork();
}

功能接口只能有一个抽象方法。如果我们尝试在其中添加一个抽象方法,则会抛出编译时错误。

classInterfaceTest {
publicstaticvoidmain(String[] args) {
// 通过匿名内部类调用WorkerInterfacework=newWorkerInterface() {
@OverridepublicvoiddoWork() {
System.out.println("通过匿名内部类调用");
            }
        };
work.doWork();
// 通过 Lambda 表达式调用// Lambda 表达式实际上是一个对象。// 我们可以将 Lambda 表达式赋值给一个变量,就可像其它对象一样调用。work= ()->System.out.println("通过 Lambda 表达式调用");
work.doWork();
    }
}

四、方法引用

1、方法引用简介

方法引用是lambda表达式的一种特殊形式,如果正好有某个方法满足一个lambda表达式的形式,那就可以将这个lambda表达式用方法引用的方式表示,但是如果这个lambda表达式比较复杂就不能用方法引用进行替换。实际上方法引用是lambda表达式的一种语法糖。

2、方法引用分类

为了演示以下代码,我们先自定义一个User类,有两个属性,name(String),age(Integer)

publicclassUser {
privateStringname;
privateIntegerage;
publicStudent(){
    }
publicUser(Stringname,Integerage){
this.name=name;
this.age=age;
    }
publicStringgetName() {
returnname;
    }
publicvoidsetName(Stringname) {
this.name=name;
    }
publicintgetAge() {
returnage;
    }
publicvoidsetAge(intage) {
this.age=age;
    }
publicstaticintcompareUserByAge(Useru1,Useru2){
returnu1.getAge() -u2.getAge();
    }
publicstaticintcompareUserByName(Useru1,Useru2){
returnu1.getName().compareToIgnoreCase(u2.getName());
    }
}

2.1、类名::静态方法名

User类有两个属性name和age并提供了初始化name和age的构造方法,并且在最下方提供了两个静态方法分别按age和name进行比较先后顺序。

接下来的需求是,按着分数由小到大排列并输出,在使用方法引用前,我们先使用lambda表达式的方式进行处理

Useru1=newUser("zhangsan",60);
Useru2=newUser("lisi",70);
Useru3=newUser("wangwu",80);
Useru4=newUser("zhaoliu",90);
List<User>userList=Arrays.asList(u1,u2,u3,u4);
userList.sort((o1, o2) ->o1.getAge() -o2.getAge());
userList.forEach(u->System.out.println(u.getAge()));

使用类名::静态方法名 方法引用替换lambda表达式

userList.sort(User::compareUserByAge);
userList.forEach(u->System.out.println(u.getAge()));

2.2、对象::实例方法名

我们再自定义一个用于比较User元素的类

publicclassComparatorUser {
publicintcompareUserByAge(Useru1,Useru2){
returnu2.getAge() -u1.getAge();
    }
}

ComparatorUser中定义了一个非静态的,实例方法compareUserByAge,同样该方法的定义满足Comparator接口的compare方法定义,所以这里可以直接使用 对象::实例方法名 的方式使用方法引用来替换lambda表达式

ComparatorUsercomparatorUser=newComparatorUser();
userList.sort(comparatorUser::compareUserByAge);
userList.forEach(u->System.out.println(u.getAge()));

2.3、类名::实例方法名

这种方法引用的方式较之前两种稍微有一些不好理解,因为无论是通过类名调用静态方法还是通过对象调用实例方法这都是符合Java的语法,使用起来也比较清晰明了。那我们带着这个疑问来了解一下这个比较特殊的方法引用。

现在再看一下Student类中静态方法的定义

public static int compareUserByAge(User u1,User u2){

   return u1.getAge() - u2.getAge();

}

虽然这个方法在语法上没有任何问题,可以作为一个工具正常使用,但是有没有觉得其在设计上是不合适的或者是错误的。这样的方法定义放在任何一个类中都可以正常使用,而不只是从属于User这个类,那如果要定义一个只能从属于User类的比较方法下面这个实例方法更合适一些

publicintcompareByAge(User u){

   returnthis.getAge() - u.getAge();

}

接收一个User对象和当前调用该方法的User对象的分数进行比较即可。现在我们就可以使用 类名::实例方法名 这种方式的方法引用替换lambda表达式了

userList.sort(User::compareByAge);

userList.forEach(u-> System.out.println(u.getAge()));

这里非常奇怪,sort方法接收的lambda表达式不应该是两个参数么,为什么这个实例方法只有一个参数也满足了lambda表达式的定义(想想这个方法是谁来调用的)。这就是 类名::实例方法名 这种方法引用的特殊之处:当使用 类名::实例方法名 方法引用时,一定是lambda表达式所接收的第一个参数来调用实例方法,如果lambda表达式接收多个参数,其余的参数作为方法的参数传递进去。

结合本例来看,最初的lambda表达式是这样的

userList.sort((u1, u2) -> u1.getAge() - u2.getAge());

那使用 类名::实例方法名 方法引用时,一定是u1来调用了compareByAge实例方法,并将u2作为参数传递进来进行比较。是不是就符合了compareByAge的方法定义。

2.4、类名::new

也称构造方法引用,和前两种类似只要符合lambda表达式的定义即可,回想下Supplier函数式接口的get方法,不接收参数有返回值,正好符合无参构造方法的定义

@FunctionalInterfacepublicinterfaceSupplier<T> {
/*** Gets a result.** @return a result*/Tget();
}
Supplier<User>supplier=User::new;

上面就是使用了User类构造方法引用创建了supplier实例,以后通过supplier.get()就可以获取一个User类型的对象,前提是User类中存在无参构造方法。

相关文章
|
5天前
|
自然语言处理 安全 Java
Aviator Java 表达式引擎
AviatorScript 是一门高性能、轻量级寄宿于 JVM 之上的脚本语言。
23 10
|
2天前
|
Java API 数据处理
探索Java中的Lambda表达式与Stream API
【10月更文挑战第22天】 在Java编程中,Lambda表达式和Stream API是两个强大的功能,它们极大地简化了代码的编写和提高了开发效率。本文将深入探讨这两个概念的基本用法、优势以及在实际项目中的应用案例,帮助读者更好地理解和运用这些现代Java特性。
|
19天前
|
Java 数据库
案例一:去掉数据库某列中的所有英文,利用java正则表达式去做,核心:去掉字符串中的英文
这篇文章介绍了如何使用Java正则表达式从数据库某列中去除所有英文字符。
32 15
|
6天前
|
Java API
[Java]Lambda表达式
本文主要介绍了Java中的Lambda表达式,包括其优化匿名内部类的方式、使用规范、内置函数式接口及方法引用等内容。文章详细解析了Lambda的基础语法、参数列表、方法体的简化规则,以及如何利用Lambda优化代码。此外,还探讨了Lambda的作用域和引用规则,强调了对局部变量、成员变量和常量的访问限制,旨在帮助读者全面理解和掌握Lambda表达式的应用。
[Java]Lambda表达式
|
15天前
|
Java 程序员 API
Java中的Lambda表达式:简化代码的秘密武器
【10月更文挑战第11天】 在Java编程中,Lambda表达式是一种简洁而强大的工具,它允许我们将函数作为参数传递给其他方法。本文将介绍Lambda表达式的基本概念、使用方法以及在实际项目中的应用案例,帮助你更好地理解和利用这一特性来简化代码。
21 8
|
9天前
|
Java 测试技术 开发者
🌟Java零基础-Lambda运算符详解 🌟
【10月更文挑战第12天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
16 1
|
18天前
|
并行计算 Java API
Java中的Lambda表达式及其应用
本文将深入探讨Java中的Lambda表达式,从基本概念到实际应用。我们将了解Lambda表达式的定义、优势和使用场景,并通过实例展示其在Java编程中的强大功能。无论是初学者还是经验丰富的开发者,都可以从中获得有价值的见解。
|
17天前
|
Java
让星星⭐月亮告诉你,jdk1.8 Java函数式编程示例:Lambda函数/方法引用/4种内建函数式接口(功能性-/消费型/供给型/断言型)
本示例展示了Java中函数式接口的使用,包括自定义和内置的函数式接口。通过方法引用,实现对字符串操作如转换大写、数值转换等,并演示了Function、Consumer、Supplier及Predicate四种主要内置函数式接口的应用。
20 1
|
21天前
|
并行计算 Java API
探索Java中的Lambda表达式:简化代码,提高可读性
【10月更文挑战第5天】Lambda表达式在Java 8中引入,旨在简化集合操作和并行计算。本文通过介绍Lambda表达式的基本概念、语法结构以及实际应用示例,展示了如何利用这一特性编写更加简洁、易读的代码。我们将从Lambda的基础入手,逐步深入到其在函数式接口中的应用,并探讨其对Java编程范式的影响。
|
21天前
|
XML Java 数据格式
Java正则表达式大全(参考)
Java正则表达式大全(参考)