【JAVA8新特性】- Lambda表达式

先来个例子热身:

        // java8中增强的Comparator
        List<Double> list = new ArrayList<>();

        list.add(12.3);
        list.add(13.3);
        list.add(14.3);
        list.add(15.3);
        list.add(16.3);
        list.add(10.3);

        // 使用Comparator接口对其从小到大排序:
        Collections.sort(list, new Comparator<Double>() {
            @Override
            public int compare(Double o1, Double o2) {
                return o1.compareTo(o2);
            }
        });

        // Comparator接口是一个函数式接口,函数描述为(T,T)->int,java8中可以使用Lambda改造上面的方法
        Collections.sort(list, (o1, o2) -> o1.compareTo(o2));

        // Java8对list 提供了sort方法,可以代替Comparator.sort
        list.sort((o1, o2) -> o1.compareTo(o2));

        // 方法进一步简化
        list.sort(Double::compareTo);

        // java8对Comparator 增加了reversed 结果反转
        Comparator<Double> comparator = Double::compareTo;
        list.sort(comparator.reversed());

        list.forEach(e -> System.out.print(e + "   "));

下面来了解Lambda

Java 8的Lambda表达式借鉴了C#和Scala等语言中的类似特性,简化了匿名函数的表达方式。

Lambda表达式可以直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例。

什么是函数式接口?简单来说就是只包含一个抽象方法的接口,允许有默认的实现(使用default关键字描述方法)。函数式接口建议使用@FunctionalInterface注解标注,虽然这不是必须的,但是这样做更符合规范。
在Java8之前,实现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表达式包含了三个部分:

  1. 参数列表;
  2. 箭头->把参数列表与Lambda主体分隔开;
  3. 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来说,查看其源码如下:

@FunctionalInterface
public 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

普通引用,方法的引用,构造函数的引用

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

@FunctionalInterface
public interface TransForm<T, R> {

    R trandForm(T t);
}

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

        TransForm<String, Integer> transForm = (String str) -> Integer.valueOf(str);
        System.out.println(transForm.trandForm("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

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

@FunctionalInterface
public 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实例:

        TransForm2<String, Apple> transForm3 = Apple::new;
        Apple greenApple = transForm3.create("green");

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

        TransForm2<String, Apple> transForm4 = (String str) -> new Apple(str);
        Apple greenApple2 = transForm4.create("green");

Lambda表达式访问变量

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

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

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

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


image.png

Lambda表达式实战

定义实体类Person

public class Person {

    public Person(String name, int age, int height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    private String name;
    private int age;
    private int height;
    ......

定义筛选接口(筛选接口)

@FunctionalInterface
public interface PersonFilter {

    boolean isChoose(Person person);
}

定义筛选方法:

public class PersonFilterMethod {

    public static List<Person> filterPersons(List<Person> persons, PersonFilter personFilter) {
        List<Person> choosePersons = new ArrayList<>();
        for (Person person : persons) {
            if (personFilter.isChoose(person)) {
                choosePersons.add(person);
            }
        }
        return choosePersons;
    }
}

筛选年龄大于25的Person

        Person person1 = new Person("python", 25, 60);
        Person person2 = new Person("java", 26, 70);
        Person person3 = new Person("php", 27, 80);
        Person person4 = new Person("js", 28, 90);

        List<Person> persons = new ArrayList<>();
        persons.add(person1);
        persons.add(person2);
        persons.add(person3);
        persons.add(person4);

        List<Person> filterPersons = PersonFilterMethod.filterPersons(persons, person -> person.getAge() > 25);

筛选年龄大于25,身高大于80的Person

 List<Person> people = PersonFilterMethod.filterPersons(persons, person -> (person.getAge() > 25 && person.getHeight() > 80));

参考博文:https://mrbird.cc/java8lambda1.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容