50字范文,内容丰富有趣,生活中的好帮手!
50字范文 > Java8 新特性 -- Lambda表达式:函数式接口 方法的默认实现和静态方法 方法引用

Java8 新特性 -- Lambda表达式:函数式接口 方法的默认实现和静态方法 方法引用

时间:2021-01-12 09:03:08

相关推荐

Java8 新特性 -- Lambda表达式:函数式接口 方法的默认实现和静态方法 方法引用

文章目录

1. Lambda表达式1.1 Lambda表达式语法1.2 Lambda表达式示例1.3 说明:函数式接口2. 方法的默认实现和静态方法3. 方法引用3.1 方法引用示例4. 注解(Annotation)5. 类型推测6. 新增Optional类7. 新增Stream类8. 调用JavaScript9. 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 parator;/*** @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;}@Overridepublic 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>() {@Overridepublic 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, paring(Person::getAge));

方法引用共有下面4种形式:

引用构造方法:ClassName::new引用类静态方法:ClassName::methodName引用特定类的任意对象方法:ClassName::methodName引用某个对象的方法:instanceName::methodName

3.1 方法引用示例

Test.java

package com.tian;import java.util.Arrays;import parator;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;}@Overridepublic 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, paring(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语言中,StreamAPI提供了非常强大的功能,使用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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}}public class Test2 {public static void main(String[] args) {ArrayList<Student> students = new ArrayList<>();// 初始化studentsstudents.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类还提供了parallelmapreduce等方法,这些方法用于增加对原生类并发处理的能力,有兴趣的读者可以参考Java官方文档学习。

8. 调用JavaScript

JDK1.8增加API使得通过Java程序来调用JavaScript代码,使用示例如下所示:

package com.tian;import javax.script.ScriptEngine;import javax.script.ScriptEngineManager;import javax.script.ScriptException;public class Test4 {public static void main(String[] args) throws ScriptException {ScriptEngineManager manager = new ScriptEngineManager();ScriptEngine engine = manager.getEngineByName("JavaScript");// 显示引擎名称System.out.println(engine.getClass().getName());// 调用JavaScript代码System.out.println(engine.eval("function f(){return 'Hello World'};" +"f();"));}}

运行结果:

9. Base64

Base64编码是一种常见的字符编码,可用来作为电子邮件或Web Service附件的传输编码。JDK1.8Base64编码添加到了标准类库中。示例代码如下所示:

package com.tian;import java.nio.charset.StandardCharsets;import java.util.Base64;public class Test3 {public static void main(String[] args) {String str = "Hello World";String encodeStr = Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8));System.out.println(encodeStr);String decodeSr = new String(Base64.getDecoder().decode(encodeStr));System.out.println(decodeSr);}}

运行结果:

Java8 新特性 -- Lambda表达式:函数式接口 方法的默认实现和静态方法 方法引用 注解 类型推测 Optional类 Stream类 调用JavaScript Base64

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。