Lambda表达式和方法引用
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lambda表达式和方法引用相关的知识,希望对你有一定的参考价值。
参考技术A 基本语法:(parameters) -> expression或(parameters) -> statements;左侧为参数,右侧为函数式接口方法的实现
lambda表达式相当于把函数作为参数传给方法,python中就可以将函数做参数传给另个函数。果然语言都在趋同。
需要函数式接口支持
当lambda体已有方法实现时可以用::进一步简化
前提:已实现的方法必须和函数式接口的抽象方法参数和返回值一致
JDK8中有双冒号的用法,就是把方法当做参数传到stream内部,使stream的每个元素都传入到该方法里面执行一下
基本语法:类名::方法名
静态方法引用(static method)语法:classname::methodname 例如:Person::getAge
对象的实例方法引用语法:instancename::methodname 例如:System.out::println
对象的超类方法引用语法: super::methodname
类构造器引用语法: classname::new 例如:ArrayList::new
数组构造器引用语法: typename[]::new 例如: String[]:new
Java8 新特性 -- Lambda表达式:函数式接口方法的默认实现和静态方法方法引用注解类型推测Optional类Stream类调用JavaScriptBase64
文章目录
- 1. Lambda表达式
- 2. 方法的默认实现和静态方法
- 3. 方法引用
- 4. 注解(Annotation)
- 5. 类型推测
- 6. 新增Optional类
- 7. 新增Stream类
- 8. 调用JavaScript
- 9. Base64
1. Lambda表达式
Lambda
表达式是一个匿名函数(指的是没有函数名的函数),它基于数学中的λ
演算得名,直接对应于其中的Lambda
抽象。
1.1 Lambda表达式语法
Lambda
表达式允许把函数作为一个方法的参数。Lambda
表达式的基本语法如下所示:
(parameters) -> expression
// 或者
(parameters) -> expression
1.2 Lambda表达式示例
Arrays.asList(1, 3, 5, 7, 9).forEach(item -> System.out.println(item));
运行结果:
以上这种写法中,i
的类型由编译器推测出来的,当然,也可以显式地指定类型,如下例所示:
Arrays.asList(1, 3, 5, 7, 9).forEach((Integer item) -> System.out.println(item));
在Java8
以前,Java
语言通过匿名函数的方法来代替Lambda
表达式。
对于列表的排序,如果列表里面存放的是自定义的类,那么通常需要指定自定义的排序方法,传统的写法如下所示:
Test.java
package com.tian;
import java.util.Arrays;
import java.util.Comparator;
/**
* @author CodeJiao
*/
class Person
private String name;
private int age;
public Person(String name, int age)
this.name = name;
this.age = age;
public String getName()
return name;
public int getAge()
return age;
@Override
public String toString()
return "Person" +
"name='" + name + '\\'' +
", age=" + age +
'';
public class Test
public static void main(String[] args)
Person[] peoples = new Person("Jack", 20), new Person("Rose", 18);
// 自定义排序方法, 通过年龄进行排序
Arrays.sort(peoples, new Comparator<Person>()
@Override
public int compare(Person person1, Person person2)
// 根据年龄升序排序
return person1.getAge() - person2.getAge();
);
for (Person people : peoples)
System.out.println(people);
运行结果:
采用Lambda
表达式后,写法如下所示:
// 自定义排序方法, 通过年龄进行排序
Arrays.sort(peoples, (person1, person2) ->
// 根据年龄升序排序
return person1.getAge() - person2.getAge();
);
或者
// 自定义排序方法, 通过年龄进行排序
Arrays.sort(peoples, (Person person1, Person person2) ->
// 根据年龄升序排序
return person1.getAge() - person2.getAge();
);
1.3 说明:函数式接口
Lambda
表达式是通过函数式接口(只有一个方法的普通接口)来实现的。函数式接口可以被隐式地转换为Lambda
表达式。为了与普通的接口区分开(普通接口中可能会有多个方法),JDK1.8
新增加了一种特殊的注解@FunctionalInterface
。下面给出一个函数式接口的定义:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface
2. 方法的默认实现和静态方法
JDK1.8
通过使用关键字default
可以给接口中的方法添加默认实现
此外,接口中还可以定义静态方法,示例代码如下所示:
public interface Test
void method1();
default void method2()
System.out.println("我是接口的默认实现方法");
static void method3()
System.out.println("我是接口中的静态方法");
那么,为什么要引入接口中方法的默认实现呢?
这样做的最重要的一个目的就是为了实现接口升级。在原有的设计中,如果想要升级接口,例如给接口中添加一个新的方法,那么会导致所有实现这个接口的类都需要被修改,这给 Java
语言已有的一些框架进行升级带来了很大的麻烦。如果接口能支持默认方法的实现,那么可以给这些类库的升级带来许多便利。例如,为了支持Lambda
表达式,Collection
中引入了foreach
方法,可以通过这个语法增加默认的实现,从而降低了对这个接口进行升级的代价,不需要所有实现这个接口的类进行修改。
3. 方法引用
方法引用指的是可以直接引用Java
类或对象的方法。它可以被看成是一种更加简洁易懂的Lambda
表达式,使用方法引用后,上例中的排序代码就可以使用下面更加简洁的方式来编写:
原来:
// 自定义排序方法, 通过年龄进行排序
Arrays.sort(peoples, (person1, person2) ->
// 根据年龄升序排序
return person1.getAge() - person2.getAge();
);
现在:
// 自定义排序方法, 通过年龄进行排序
Arrays.sort(peoples, Comparator.comparing(Person::getAge));
方法引用共有下面4种形式:
- 引用构造方法:
ClassName::new
- 引用类静态方法:
ClassName::methodName
- 引用特定类的任意对象方法:
ClassName::methodName
- 引用某个对象的方法:
instanceName::methodName
3.1 方法引用示例
Test.java
package com.tian;
import java.util.Arrays;
import java.util.Comparator;
import java.util.function.Supplier;
/**
* @author CodeJiao
*/
class Person
private String name;
private int age;
public Person()
public Person(String name, int age)
this.name = name;
this.age = age;
public static Person getInstance(final Supplier<Person> supplier)
return supplier.get();
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 static int compareByAge(Person a, Person b)
return b.age - a.age;
@Override
public String toString()
return "Person" +
"name='" + name + '\\'' +
", age=" + age +
'';
class CompareProvider
public int compareByAge(Person a, Person b)
return a.getAge() - b.getAge();
public class Test
public static void main(String[] args)
// 引用构造方法
Person p1 = Person.getInstance(Person::new);
p1.setAge(22);
System.out.println("测试引用构造方法:" + p1.getAge());
Person[] peoples = new Person("Jack", 20), new Person("Rose", 18);
// 引用特定类的任意对象方法
Arrays.sort(peoples, Comparator.comparing(Person::getAge));
System.out.println("测试引用特定类的任意对象方法:");
for (Person people : peoples)
System.out.println(people);
// 引用静态方法
Arrays.sort(peoples, Person::compareByAge);
System.out.println("测试引用静态方法:");
for (Person people : peoples)
System.out.println(people);
// 引用某个对象的方法
Arrays.sort(peoples, new CompareProvider()::compareByAge);
System.out.println("测试引用某个对象的方法:");
for (Person people : peoples)
System.out.println(people);
运行结果:
4. 注解(Annotation)
- 1)
JDK1.5
中引入了注解机制,注解为开发人员在代码中添加信息提供了一种形式化的方法,它使得开发人员可以在某个时刻方便地使用这些数据(通过解析注解来使用这些数据)。注解的语法比较简单,除了@
符号的使用以外,它基本上与Java
语言的语法一致,Java
语言内置了三种注解方式,它们分别是@Override
(表示当前方法是覆盖父类的方法)、@Deprecated
(表示当前元素是不赞成使用的)、@SuppressWarnings
(表示关闭一些不当的编译器警告信息)。需要注意的是,它们都定义在java.lang
包中。 - 2)
JDK1.8
对注解进行了扩展。使得注解被使用的范围更广,例如可以给局部变量、泛型、方法异常等提供注解。
5. 类型推测
JDK1.8加强了类型推测机制,这种机制可以使得代码更为简洁,假如有如下类的定义。
在调用的时候,可以使用下面的代码:
在 Java 7的时候,这种写法将会产生编译错误,Java 7中的正确写法如下所示:
同理,在调用cons方法的时候的写法为:
在 Java 7
的时候需要显式地指定类型:List.cons(5,List.<Integer>nil());
6. 新增Optional类
在使用Java
语言进行编程的时候,经常需要使用大量的代码来处理空指针异常,而这种操作往往会降低程序的可读性。JDK1.8
引入了Optional
类来处理空指针的情况,从而增强了代码的可读性,下面给出一个简单的例子:
示例代码:
package com.tian;
import java.util.Optional;
public class Test1
public static void main(String[] args)
Optional<String> s1 = Optional.of("Hello");
// 判断是否有值
if (s1.isPresent())
System.out.println(s1.get());
Optional<String> s2 = Optional.ofNullable(null);
// 判断是否有值
if (s2.isPresent())
System.out.println(s2.get());
运行结果:
7. 新增Stream类
JDK1.8
新增加了Stream
类,从而把函数式编程的风格引入到Java
语言中,Stream
的 API
提供了非常强大的功能,使用Stream
后,可以写出更加强大、更加简洁的代码(例如可以代替循环控制语句)。示例代码如下所示:
package com.tian;
import java.util.*;
import java.util.stream.Collectors;
class Student
private String name;
private Integer age;
public Student(String name, Integer age)
this.name = name;
this.age = age;
public String getName()
return name;
public Integer getAge()
return age;
@Override
public String toString()
return "Student" +
"name='" + name + '\\'' +
", age=" + age +
'';
public class Test2
public static void main(String[] args)
ArrayList<Student> students = new ArrayList<>();
// 初始化students
students.add(new Student("张有余", 10));
students.add(new Student("雷磊", 13));
students.add(new Student("侯家领", 10));
students.add(new Student("徐曦", 15));
System.out.println("找出年龄为10的第一个学生");
Optional<Student> s = students.stream().filter(student ->
student.getAge() == 10
).findFirst();
if (s.isPresent())
System.out.println(s.toString());
System.out.println("找出年龄为10的所有学生");
List<Student> listStu = students.stream().filter(student -> student.getAge() == 10).collect(Collectors.toList());
listStu.forEach(stu ->
System.out.println(stu.toString());
);
System.out.println("对学生按照年龄分组");
Map<Integer, List<Student>> map = students.stream().collect(Collectors.groupingBy(Student::getAge));
Iterator<Map.Entry<Integer, List<Student>>> iterator = map.entrySet().iterator();
while (iterator.hasNext())
Map.Entry<Integer, List<Student>> entry = iterator.next();
Integer age = entry.getKey();
System.out.println("age: " + age);
List<Student> group = entry.getValue();
for (Student student : group)
System.out.println(student.getName() + " ");
运行结果:
此外,Stream
类还提供了parallel
、map
、reduce
等方法,这些方法用
以上是关于Lambda表达式和方法引用的主要内容,如果未能解决你的问题,请参考以下文章
java8新特性→方法和构造函数引用:替代Lambda表达式
Java8 新特性 -- Lambda表达式:函数式接口方法的默认实现和静态方法方法引用注解类型推测Optional类Stream类调用JavaScriptBase64