【Java8实战】Lambda表达式(一)

Java 8的Lambda表达式借鉴了C#和Scala等语言中的类似特性,简化了匿名函数的表达方式。Lambda表达式可以直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例。什么是函数式接口?简单来说就是只包含一个抽象方法的接口,允许有默认的实现(使用default关键字描述方法)。函数式接口建议使用@FunctionalInterface注解标注,虽然这不是必须的,但是这样做更符合规范。

在Java 8之前,实现Runnable常用方式是编写一个匿名类:

Thread thread = new Thread(new Runnable() {    @Override    public void run() {        System.out.println("hello");    }});thread.start();

使用Lambda表达式后,上面的代码可以改造为:

Thread thread = new Thread(() -> System.out.println("hello"));thread.start();

是不是很神奇?!很简洁?!

Lambda表达式解析

Lambda表达式的基本语法如下:

(parameters) -> expression or(parameters) -> { statements; }

由语法可以看到,Lambda表达式包含了三个部分:

  • 参数列表;
  • 箭头->把参数列表与Lambda主体分隔开;
  • Lambda主体,只有一行代码的时候可以省略大括号和return关键字。

比如下面这些Lambda表达式都是合法的:

(String str) -> str.length()(String str) -> { return str.length(); }?() -> System.out.println("hello")?() -> {}() -> 17?(int x, int y) -> {    System.out.println(x);    System.out.println(y);}

Lambda的使用场合

什么时候可以使用Lambda表达式?使用Lambda必须满足以下两个条件:

  1. 实现的对象是函数式接口的抽象方法;
  2. 函数式接口的抽象方法的函数描述符和Lambda表达式的函数描述符一致。

函数式接口

函数式接口的定义开头已经说了,这里就不再赘述。在Java 8之前,常见的函数式接口有java.util.Comparator,java.lang.Runnable等。拿java.util.Runnable来说,查看其源码如下:

@FunctionalInterfacepublic interface Runnable {    public abstract void run();}

这个接口只有一个抽象方法,并且使用@FunctionalInterface注解标注。

接口现在还可以拥有默认方法(即在类没有对方法进行实现时,其主体为方法提供默认实现的方法)。哪怕有很多默认方法,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。

函数描述符

函数描述符其实也可以理解为方法的签名。比如上述的Runnable抽象方法不接受参数,并且返回void,所以其函数描述符为() -> void。而() -> System.out.println("hello")Lambda表达式也是不接受参数,并且返回void,即其函数描述符也是() -> void。所以代码Runnable r = () -> System.out.println("hello");是合法的。

特殊的void兼容规则

如果一个Lambda的主体是一个语句表达式, 它就和一个返回void的函数描述符兼容(当然需要参数列表也兼容)。例如,以下Lambda是合法的,尽管List的add方法返回了一个 boolean,而不是Runnable抽象方法函数描述符() -> void所要求的void:

List<String> list = new ArrayList<>();Runnable r = () -> list.add("hello");

更简洁的Lambda

编写一个类型转换的函数式接口:

@FunctionalInterfacepublic interface TransForm<T, R> {    R transForm(T t);}

编写一个Lambda表达式实现该函数式接口,用于实现String转换为Integer,代码如下:

TransForm<String, Integer> t = (String str) -> Integer.valueOf(str);System.out.println(t.transForm("123"));

上面的Lambda表达式可以进一步简化为如下方式:

TransForm<String, Integer> t = (str) -> Integer.valueOf(str);System.out.println(t.transForm("123"));

因为Java编译器会从上下文(目标类型)推断出用什么函数式接口来配合Lambda表达式,这意味着它也可以推断出适合Lambda的签名。就拿这个例子来说,TransForm的抽象方法transForm在本例中的函数描述符为(String) -> Integer,所以对应的Lambda的签名也是如此,即Lambda的参数即使不声名类型,Java编译器可以知道其参数实际上为String类型。

其实,上面的Labmda表达式还不是最简洁的,其还可以更进一步地简化为如下写法:

TransForm<String, Integer> t = Integer::valueOf;System.out.println(t.transForm("123"));

你肯定很困惑,这还是Lambda表达式吗,箭头去哪里了?双冒号又是什么鬼?其实这种写法有一个新的名称,叫做方法的引用

方法引用可以被看作仅仅调用特定方法的Lambda的一种快捷写法。它的基本思想是,如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它,这样代码可读性更好。基本写法就是目标引用放在分隔符::前,方法的名称放在后面。

举几个Lambda及其等效方法引用的例子:

Lambda表达式 等效方法引用
(String s) -> System.out.println(s) System.out::println
(str, i) -> str.substring(i) String::substring
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack

符号::除了出现在方法的引用外,它还常见于构造函数的引用中。为了演示什么是构造函数的引用,我们创建一个新的函数式接口:

@FunctionalInterfacepublic interface Generator<T, R> {    R create(T t);}

创建一个Apple类:

public class Apple {    public Apple(String color) {        this.color = color;    }    private String color;?    public String getColor() {        return color;    }    public void setColor(String color) {        this.color = color;    }}

现在我们可以使用如下方式来创造一个Apple实例:

Generator<String, Apple> g = Apple::new;Apple apple = g.create("red");

这种通过ClassName::new的写法就是构造函数的引用。在这里Generator的抽象方法接收一个String类型参数,返回值类型为Apple,这和Apple类的构造函数相符合,所以这里编译可以通过。它等价于下面的写法:

Generator<String, Apple> g = (color) -> new Apple(color);Apple apple = g.create("red");

Lambda表达式访问变量

Lambda表达式可以访问局部final变量,成员变量和静态变量。

这里主要说下局部final变量。有无final关键字不重要,重要的是确保该变量的值不会被改变就行了。比如下面的例子可以编译通过:

String hello = "hello lambda";Runnable r = () -> System.out.println(hello);

而下面的这个就会编译出错,因为变量hello的值被改变了:

Lambda表达式实战

假如现在有如下需求:现有一个包含了各种颜色不同重量的苹果的List,编写一个方法,从中筛选出满足要求的苹果。比如筛选出红色的苹果、红色并且重量大于1kg的苹果、绿色重量小于0.5kg的苹果或者红色大于0.5kg的苹果等等。

不使用Lambda

在没有接触Lambda之前,我们一般会这样做:

定义一个筛选的接口

import cc.mrbird.java8.domain.Apple;?public interface AppleFilter {    boolean test(Apple apple);}

然后根据筛选的条件来编写各个不同的实现类:

筛选出红色苹果的实现方法:

import cc.java8.domain.Apple;?public class RedApple implements AppleFilter {    @Override    public boolean test(Apple apple) {        return "red".equalsIgnoreCase(apple.getColor());    }}

筛选出红色并且重量大于1kg的苹果的实现方法:

import cc.java8.domain.Apple;?public class RedAndMoreThan1kgApple implements AppleFilter {    @Override    public boolean test(Apple apple) {        return "red".equalsIgnoreCase(apple.getColor()) && apple.getWeight() > 1.0;    }}

筛选出绿色重量小于0.5kg的苹果或者红色大于0.5kg的苹果的实现方法:

import cc.java8.domain.Apple;?public class GreenAndLessThan05OrRedAndMoreThan05Apple implements AppleFilter {    @Override    public boolean test(Apple apple) {        return ("green".equalsIgnoreCase(apple.getColor()) && apple.getWeight() < 0.5)                || ("red".equalsIgnoreCase(apple.getColor()) && apple.getWeight() > 0.5);    }}

筛选苹果的方法:

import cc.java8.domain.Apple;import java.util.ArrayList;import java.util.List;?public class AppleFilterMethod {    public static List<Apple> filterApple(List<Apple> list, AppleFilter filter) {        List<Apple> filterList = new ArrayList<>();        for (Apple apple : list) {            if (filter.test(apple)) {                filterList.add(apple);            }        }        return filterList;    }}

开始筛选苹果:

List<Apple> appleList = new ArrayList<>();appleList.add(new Apple("red", 0.4));appleList.add(new Apple("red", 0.6));appleList.add(new Apple("red", 1.3));appleList.add(new Apple("green", 0.2));appleList.add(new Apple("green", 0.35));appleList.add(new Apple("green", 1.1));?List<Apple> appleFilterList = AppleFilterMethod.filterApple(appleList, new RedApple());for (Apple apple : appleFilterList) {    System.out.println(apple.getColor() + " apple,weight:" + apple.getWeight());}

输出:

red apple,weight:0.4red apple,weight:0.6red apple,weight:1.3

剩下的略。

可以看到,我们为了满足各种筛选条件创造了各种筛选接口的实现类,真正起作用的只有筛选方法中return那一行代码,剩下的都是一些重复的模板代码。使用Java 8中的Lambda可以很好的消除这些模板代码。

使用Lambda

AppleFilter接口实际上就是一个函数式接口,所以它的各种实现可以用Lambda表达式来替代,而无需真正的去写实现方法。

定义筛选接口:

import cc.java8.domain.Apple;?public interface AppleFilter {    boolean test(Apple apple);}

筛选苹果的方法:

import cc.mrbird.java8.domain.Apple;import java.util.ArrayList;import java.util.List;?public class AppleFilterMethod {    public static List<Apple> filterApple(List<Apple> list, AppleFilter filter) {        List<Apple> filterList = new ArrayList<>();        for (Apple apple : list) {            if (filter.test(apple)) {                filterList.add(apple);            }        }        return filterList;    }}

接下来便可以开始筛选了:

筛选红色的苹果:

List<Apple> appleFilterList = AppleFilterMethod.filterApple(appleList,        (apple) -> "red".equalsIgnoreCase(apple.getColor()));for (Apple apple : appleFilterList) {    System.out.println(apple.getColor() + " apple,weight:" + apple.getWeight());}

输出:

red apple,weight:0.4red apple,weight:0.6red apple,weight:1.3

筛选出红色并且重量大于1kg的苹果:

 List<Apple> appleFilterList = AppleFilterMethod.filterApple(appleList,                (apple) -> "red".equalsIgnoreCase(apple.getColor()) && apple.getWeight() > 1.0);for (Apple apple : appleFilterList) {    System.out.println(apple.getColor() + " apple,weight:" + apple.getWeight());}

输出:

red apple,weight:1.3

筛选出绿色重量小于0.5kg的苹果或者红色大于0.5kg的苹果:

List<Apple> appleFilterList = AppleFilterMethod.filterApple(appleList,        (apple) -> ("green".equalsIgnoreCase(apple.getColor()) && apple.getWeight() < 0.5) ||                   ("red".equalsIgnoreCase(apple.getColor()) && apple.getWeight() > 0.5));for (Apple apple : appleFilterList) {    System.out.println(apple.getColor() + " apple,weight:" + apple.getWeight());}

输出:

red apple,weight:0.6red apple,weight:1.3green apple,weight:0.2green apple,weight:0.35

使用Lambda表达式消除了大量的样板代码,并且可以灵活的构造筛选条件!

原文地址:https://www.cnblogs.com/7788IT/p/11625512.html

时间: 2024-10-10 06:48:24

【Java8实战】Lambda表达式(一)的相关文章

java8之lambda表达式(方法引用)

有些时候,你想要传递给其他代码的操作已经有实现的方法了.示例: button.setOnAction(event -> System.out.println(event); 如果你能够只将println方法传递给setOnAction方法,就更好了!下面是改后的代码: button.setOnAction(System.out::println); 表达式System.out::println是一个方法引用,等同于lambda表达式: x -> System.out.println(x) 正如

java8之lambda表达式(变量作用域)

通常,我们希望能够在lambda表达式的闭合方法或类中访问其他的变量,例如: package java8test; public class T1 {     public static void main(String[] args) {         repeatMessage("Hello", 20);     }     public static void repeatMessage(String text,int count){         Runnable r = 

java8之lambda表达式(1)-基本语法

参考:http://www.cnblogs.com/andywithu/p/7344507.html lambda表达式,即带有参数的表达式,为更清晰地理解lambda表达式,先看如下例子: (1) class Student{ private String name; private Double score; public Student(String name, Double score) { this.name = name; this.score = score; } public S

JAVA8之lambda表达式详解,及stream中的lambda使用

原文:http://blog.csdn.net/jinzhencs/article/details/50748202 lambda表达式详解 一.问题 1.什么是lambda表达式? 2.lambda表达式用来干什么的? 3.lambda表达式的优缺点? 4.lambda表达式的使用场景? 5.lambda只是一个语法糖吗? 二.概念 lambda表达式是JAVA8中提供的一种新的特性,它支持Java也能进行简单的"函数式编程". 它是一个匿名函数,Lambda表达式基于数学中的λ演算

java8之lambda表达式入门

1.基本介绍 lambda表达式,即带有参数的表达式,为了更清晰地理解lambda表达式,先上代码: 1.1 两种方式的对比 1.1.1 方式1-匿名内部类 class Student{ private String name; private Double score; public Student(String name, Double score) { this.name = name; this.score = score; } public String getName() { ret

java8之lambda表达式(函数式接口)

在Java中有许多已有的接口都需要封装代码块,例如:Runnable或者Comparator.lambda表达式与这些接口是向后兼容的.对于只包含一个抽象方法的接口,你可以通过lambda表达式来创建该接口的对象,这种接口被称为函数式接口.注意:Java8中接口可以声明非抽象的方法. 为了演示函数式接口转换,我们以Arrays.sort方法为例.该方法的第二个参数需要一个Comparator接口(该接口只含有一个方法)的实例.接下来我们编写一个简单的lambda表达式: Arrays.sort(

Java8的Lambda表达式简介

先阐述一下JSR(Java Specification Requests)规范,即Java语言的规范提案.是向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求.任何人都可以提交JSR,可以向Java平台增添新的API和服务.JSR已成为Java界的一个重要标准.可以理解为JSR是Java开发者以及授权者指定的标准,而Java开发者以及授权者形成一个JCP国际组织.职能是制定Java标准,即所有的规范都是有迹可循的. 在Java8中推出的lambda表达式

java8之lambda表达式看这一篇就够了

java8增加了许多新特性,其中lambda表达式可以说为最重要的特性之一,本文将从如下几个方面来学习lambda:     1.lambda表达式的基本定义   2.lambda表达式的语法   3.lambda表达式基本示例 一.何为lambda表达式? 简单点说lambda为一种匿名函数,它既没有名字也没有声明的方法.访问修饰符和返回值等.它允许将函数作为方法的参数进行传递. 二.基本语法:lambda通常包含如下几种书写方式 () -> expression () -> {  stat

Java8 之 lambda 表达式、方法引用、函数式接口、默认方式、静态方法

今天我来聊聊 Java8 的一些新的特性,确实 Java8 的新特性的出现,给开发者带来了非常大的便利,可能刚刚开始的时候会有点不习惯的这种写法,但是,当你真正的熟悉了之后,你一定会爱上这些新的特性的,这篇文章就来聊聊这些新特性. lambda 表达式 lambda 表达式在项目中也是用到了,这种新的语法的加入,对于使用 Java 多年的我,我觉得是如虎添翼的感觉哈,这种新的语法,大大的改善了以前的 Java 的代码,变得更加的简洁,我觉得这也是为什么 Java8 能够很快的流行起来的原因吧.

java8之lambda表达式

lambda表达式是java8提供了一个比较重要的新特性之一,简化了很多代码的编写. 1.先看一个简单的例子,不是用lambda表达式创建一个线程: Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("hello world"); } }); 在Thread构造函数内传入一个Runnable接口的实现类. 2.再看一下使用lambda表达式创建线程的例