热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java8新特性:Lambda表达式、函数式接口以及方法引用

文章目录Java8新特性:Lambda表达式1.Lambda表达式概念2.Lambda表达式语法3.函数式接口(FunctionalInterface)4.Lambd

文章目录

  • Java8新特性:Lambda表达式
    • 1. Lambda表达式概念
    • 2. Lambda表达式语法
    • 3. 函数式接口(Functional Interface)
    • 4. Lambda表达式精简语法
    • 5. 方法引用(Method Reference)
        • 5.1 实例方法引用
        • 5.2 静态方法引用
        • 5.3 构造方法引用


Java8新特性:Lambda表达式

1. Lambda表达式概念

首先 Lambda 表达式是Java 8引入的重要新特性。

Lambda 是一个匿名函数(Lambda 表达式简化了匿名内部类的形式,并且可以达到同样的效果,当然Lambda 要优雅得多),我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码 像数据一样进行传递)。

lambda表达式与匿名函数虽然最终达到的效果是一样的,但其底层实现原理却并不相同:

  • 匿名内部类在编译之后会创建一个新的匿名内部类出来
  • 而 Lambda 是调用 JVM invokedynamic指令实现的,并不会产生新类。

还有一个重点:Lambda表达式返回的是接口对象实例

其基本语法如下:

(参数列表) -> {方法体
};

其中->是Lambda运算符,它的英文名是goes to,嗯,就是转到的意思

整个的lambda表达式等价于:

new 接口名() {// 实现接口的方法@Overridepublic void method() {// 函数逻辑}
};

看下面以Lambda语法创建线程和匿名内部类创建线程的例子

package com.yyl.lambda;public class Test {public static void main(String[] args) {// 用匿名内部类的方式来创建线程new Thread(new Runnable() {@Overridepublic void run() {System.out.println("公众号:毒力舌---点关注,不迷路!");}}).start();// 使用Lambda来创建线程new Thread(() -> System.out.println("公众号:毒力舌---点关注,不迷路!")).start();}
}

结果是一样的:

image-20220915105845703

2. Lambda表达式语法

接口中函数的声明总共6种情况:

  • 接口方法无返回值有返回值分2种
  • 其中无参数单个参数多个参数又分3种情况。

两两结合,2×3=6,一种有六种

如下六种函数示例代码:

interface I01 {void method();
}
interface I02 {void method(int a);
}
interface I03 {void method(int a, int b);
}
interface I04 {int method();
}
interface I05 {int method(int a);
}
interface I06 {int method(int a, int b);
}

我们使用lambda表达式重写这几个方法并运行:

public class Test {public static void main(String[] args) {I01 i01 = () -> {System.out.println("无返回值、无参数");};I02 i02 = (int a) -> {System.out.println("无返回值,单个参数。a=" + a);};I03 i03 = (int a, int b) -> {System.out.println("无返回值,多个参数。a=" + a + ",b=" + b);};I04 i04 = () -> {System.out.println("有返回值、无参数");return 4;};I05 i05 = (int a) -> {System.out.println("有返回值,单个参数。a=" + a);return 5;};I06 i06 = (int a, int b) -> {System.out.println("有返回值,多个参数。a=" + a + ",b=" + b);return 6;};i01.method();i02.method(5);i03.method(5,10);System.out.println(i04.method());System.out.println(i05.method(5));System.out.println(i06.method(5, 10));}
}

输出:

无返回值、无参数
无返回值,单个参数。a=5
无返回值,多个参数。a=5,b=10
有返回值、无参数
4
有返回值,单个参数。a=5
5
有返回值,多个参数。a=5,b=10
6

image-20220915130041118

看完以上代码已经能大概了解lambda表达式的用法了。

不过细心的你已经发现,要是这些接口里面定义的方法不止一个呢?那岂不是无法确定Lambda表达式究竟是要实现哪一个接口方法?

这里就要提到函数式接口的概念

3. 函数式接口(Functional Interface)

Java 8为了使现有的函数更加友好地支持Lambda表达式,引入了函数式接口的概念。

函数式接口本质上是一个仅有一个抽象方法的普通接口,所以又叫SAM接口(Single Abstract Method Interface)。

函数式接口在实际使用过程中很容易出错,比如某人在接口定义中又增加了另一个方法,则该接口不再是函数式接口,此时将该接口转换为Lambda表达式会报错。为了克服函数式接口的脆弱性,并且能够明确声明接口是作为函数式接口的意图,Java 8增加了**@FunctionalInterface**注解来标注函数式接口。

使用@FunctionalInterface注解标注的接口必须是函数式接口,也就是说该接口中只能声明一个抽象方法,如果声明多个抽象方法就会报错。但是默认方法和静态方法不属于抽象方法,因此在函数式接口中也可以定义默认方法和静态方法。

比如这样声明一个函数式接口是被允许的:

@FunctionalInterface
interface InterfaceDemo {void method(int a);// 允许定义静态方法static void staticMethod() {...}// 允许定义默认方法default void defaultMethod() {...}
}

@FunctionalInterface注解不是必须的,如果一个接口符合"函数式接口"的定义,那么加不加该注解都没有影响,就像下面的接口。

interface InterfaceDemo2 {void method(int a);
}

当然加上该注解能够更好地让编译器进行检查,也能提高代码的可读性。

4. Lambda表达式精简语法


  1. 参数类型可以省略

    比如I02 i02 = (int a) -> {System.out.println(...);};可以写成I02 i02 = (a) -> {System.out.println(...);};

  2. 假如只有一个参数,那么()括号可以省略

    比如I02 i02 = (a) -> {System.out.println(...);};可以写成I02 i02 = a -> {System.out.println(...);};

  3. 假如方法体只有一条语句,那么语句后的;分号和方法体的{}大括号可以一起省略

    比如I02 i02 = a -> {System.out.println(...);};可以写成I02 i02 = a -> System.out.println(...);

  4. 如果方法体中唯一的语句是return返回语句,那么在省略第3种情况的同时,return也必须一起省略

    比如I05 i05 = a -> {return 1;};可以写成``I05 i05 = a -> 1;`


5. 方法引用(Method Reference)

在Java 8中可以用方法引用来进一步简化Lambda表达式。(虽然两者在底层实现原理上略有不同,但在实际使用中完全可以视为等价)
有时候多个Lambda表达式的实现函数是一样的,我们可以封装成一个通用方法,再通过方法引用来实现接口

这话说的吧,就很难理解,我是这样理解的:因为是函数式接口嘛,所以接口里面就一个要实现的函数,然后我们可以通过lambda表达式来实现这个接口,进而实现其中的方法,也可以直接引用一个方法来作为这个方法的实现,然后这样就能实现这个接口对象了。

方法引用可以引用三种方法,他们的语法如下:

  • 如果是实例方法:对象名::实例方法名
  • 如果是静态方法:类名::实例方法名
  • 如果是构造方法:类名::new

下面通过例子来理解一下方法引用的流程:

5.1 实例方法引用

示例代码:

public class Test {public void eat(int a) {System.out.println("吃东西。" + "a=" + a);}public static void main(String[] args) {//Lambda表达式写法:Dog dog1 = (a) -> System.out.println("吃东西。" + "a=" + a);Cat cat1 = (a) -> System.out.println("吃东西。" + "a=" + a);dog1.doSomething(5);cat1.doSomething(5);//方法引用写法:Test test = new Test();Dog dog2 = test::eat;Cat cat2 = test::eat;dog2.doSomething(10);cat2.doSomething(10);}
}
@FunctionalInterface
interface Dog {void doSomething(int a);
}
@FunctionalInterface
interface Cat {void doSomething(int a);
}

输出结果:

吃东西。a=5
吃东西。a=5
吃东西。a=10
吃东西。a=10

5.2 静态方法引用

示例代码:

package com.yyl.lambda;public class Test {public static void eat(int a) {System.out.println("吃东西。" + "a=" + a);}public static void main(String[] args) {//Lambda表达式写法:Dog dog1 = (a) -> System.out.println("吃东西。" + "a=" + a);Cat cat1 = (a) -> System.out.println("吃东西。" + "a=" + a);dog1.doSomething(5);cat1.doSomething(5);//方法引用写法:Test test = new Test();Dog dog2 = Test::eat;Cat cat2 = Test::eat;dog2.doSomething(10);cat2.doSomething(10);}
}@FunctionalInterface
interface Dog {void doSomething(int a);
}
@FunctionalInterface
interface Cat {void doSomething(int a);
}

5.3 构造方法引用

如果函数式接口的实现恰好可以通过调用一个类的构造方法来实现(比如说接口方法与这个构造方法的参数个数、参数类型和返回值都对的上),那么就可以使用构造方法引用。

代码如下:

public class Test {public void eat(int a) {System.out.println("吃东西。" + "a=" + a);}public static void main(String[] args) {//Lambda表达式写法:DogService dogService1 = (name, age) -> new Dog(name, age);System.out.println(dogService1.getDog("大黄", 5));//方法引用写法:DogService dogService2 = Dog::new;System.out.println(dogService2.getDog("二黑", 3));}
}
@FunctionalInterface
interface DogService {Dog getDog(String name, int age);
}
class Dog {String name;int age;public Dog(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Dog{" +"name='" + name + '\'' +", age=" + age +'}';}
}

运行结果如下:

Dog{name='大黄', age=5}
Dog{name='二黑', age=3}


推荐阅读
author-avatar
碎蜂CYM夜一
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有