Java Lambda 表达式的奇幻之旅

JDK 8 对 Lambda 函数编程的支持,浅的来说无非是引入了一些新的语法结构,是继JDK5 引入的Generics后又一项对大家编码方式的一种革新,如果你不跟上的话,恐怕过段时间,你会认为Java代码成了火星语。深的来说,Java是在语言级进一步支持多核CPU的环境下的并行处理,这在Stream API 中可见一斑,在Java之前,已经有很多主流语言,像 C#和C++,支持Lambda 函数编程,此次Java引入Lambda支持也算后知后觉了。

想在Java中书写Lambda的代码,有两个东东要了解。一个是Lambda 表达式。另一个就是函数接口。这篇文章主要是针对这两点进行展开。

  • Lambda表达式 - Lambda表达式其实就是一个匿名的方法。这个方法还不能自己执行,它只能用来实现定义在函数接口中方法,当Lambda 表达式实现一个函数接口时会产生一个匿名类。
  • 函数接口 - 函数接口是一个包含且只包含一个抽象方法的接口,在JDK 8 之前,定义在接口中的方法都是抽象方法。从JDK 8 之后,接口中定义的方法可以使用default 关键字为接口方法定义默认实现。一旦定义了默认实现,就不能叫做抽象方法了。Java之前定义的Runnable,Comparable接口都可以称为函数接口。Lambda表达式在Java里脱离了函数接口,就只能算一个不能执行的表达式而已。直到Lambda表达式被用来实现某个函数接口,才能在Java 世界里运行。

让我们结合函数接口来分析一下Lambda表达式。

Lambda 表达方式可以表示为 (p1,p2,….)  -> 简单表达式|代码块

其中  ->  是 Lambda 操作符,左侧是表达式需要的参数列表,用逗号分隔,如果没有参数的话,就是一个空括号()。 右侧是表达式的具体内容,分为简单表达式和包含复杂逻辑的代码块两种。 简单表达式只能包含一个单一的表达式,但代码块就是一个匿名代码块,能包含更复杂的逻辑。

我们来看两个简单表达式的例子吧

()-> “Hello, Lambda”

这个简单的例子不接受任何参数,所以左边只有一个括号。右边是一个返回简单字符串的表达式。类似于我们以前写的如下Java 代码

String sayHello(){
  Return “Hello, Lambda”;
}

第一个函数接口与Lambda表达式结合的例子,能够运行。

interface Hello4Lambda {
    public String sayHello();
}

public class HelloLambda {
    public static void main(String[] args){
        Hello4Lambda hl  = () ->  "Hello,Lambda.";
        System.out.println(hl.sayHello());
    }
}

再看一个带参数的例子

(name) -> “Hello, ” + name

这个Lambda表达式相当于如下Java 代码

String sayHello(String name){
  Return “Hello, ” + name;
}

可运行的代码如下

interface Hello4Anyone {
    public String sayHello(String name);
}

public class HelloAnyone {
    public static void main(String[] args){
        Hello4Anyone hw  = (name) ->  "Hello," + name;
        System.out.println(hw.sayHello("Matt"));
    }
}

你可能发现Lambda表达式里的参数并没有指定参数的类型。Java会自动根据该Lambda表达式所实现的函数接口自行推演出参数类型。对于简单表达式,是一定要默认返回运算结果的。这就意味着被实现的函数接口必须有返回值而不能是void. 如下代码会报编译错误,因为接口定义的方法是一个没有返回值的接口方法。

interface Hello4Anyone {
    public void sayHello(String name);
}

public class HelloAnyone {
    public static void main(String[] args){
        Hello4Anyone hw  = (name) ->  "Hello," + name;
        System.out.println(hw.sayHello("Matt"));
    }
}

让我们看看带有默认实现的例子,这个例子会发生编译错误,因为我们定义的接口不是一个函数接口,没有抽象方法。

interface Hello4Anyone {

    public default String sayHello(String name){ //默认实现
        return "Hello, " + name;
    };
}

public class HelloAnyone {
    public static void main(String[] args){
        Hello4Anyone hw  = (name) ->  "Hello," + name;//编译错误发生
        System.out.println(hw.sayHello("Matt"));
    }
}

Lambda表达式中复杂代码块可以让我们完成比简单表达式更复杂的操作,我们只需要将代码置于{}之中,并要在代码的最后显示地加上return关键字将计算结果返回,当然是你真地需要返回结果的情况下。如果你试图实现的函数接口不需要返回任何返回值,那么你的Lambda代码块里也就不需要return 返回任何结果。

interface DoubleCalculate {
    double Sum(double[] arr);
}
public class DoubleCalculator {

    public static void main(String[] args){

        double[] a = new double[10];
        for (int i = 0 ;i< a.length;i++)
            a[i] = Math.random() * 100;
        DoubleCalculate dc = (arr) -> {
            double sum = 0;
            for (int j = 0;j<arr.length;j++){
               sum += arr[j];
           }
           return sum;
        };

        System.out.println(dc.Sum(a));

    }
}

这个例子是对一个double数组内的元素进行求和。我们使用Lambda表达式的代码块来实现该逻辑。但如果我们试图想对int 数组进行求和时,接口DoubleCalculate时无法接收int类型数组。为此,我们可能还要另外定义一个接口函数专门针对int类型数组。

interface IntCalculate {
    int Sum(int[] arr);
}

public class IntCalculator {

    public static void main(String[] args){

    int[] a = new int[10];

    for (int i = 0 ;i< a.length;i++)
        a[i] = (int)(Math.random() * 100);

        IntCalculate dc = (arr) -> {
        int sum = 0;
        for (int j = 0;j<arr.length;j++){
            sum += arr[j];
        }
        return sum;
    };

    System.out.println(dc.Sum(a));

}
}

这样代码会显得很冗余,那我们能否使用范型将函数接口中抽象方法的参数范化,这样不就可以了?答案时肯定的,JDK 5 范型的支持,同样可以应用在函数接口上。

interface Calculate<T> {
    T Sum(T[] arr);
}

public class GenericCalculator {
    public static void main(String[] args){

        //For double array .
        Double[] a = new Double[10];
        for (int i = 0 ;i< a.length;i++)
            a[i] = Math.random() * 100;

        Calculate<Double> dc = (arr) -> {
            double sum = 0;
            for (int j = 0;j<arr.length;j++){
                sum += arr[j];
            }
            return sum;
        };

        System.out.println(dc.Sum(a));

        //For int array .
        Integer[] b = new Integer[10];
        for (int i = 0 ;i< b.length;i++)
            b[i] = (int)(Math.random() * 100);

        Calculate<Integer> ic = (arr) -> {
            int sum = 0;
            for (int j = 0;j<arr.length;j++){
                sum += arr[j];
            }
            return sum;
        };

        System.out.println(ic.Sum(b));

    }
}

但对于Lambda表达式本身是无法支持范型的,我们看到了两个不同的Lambda 表达式的代码块分别针对Double 和Integer.

时间: 2024-08-03 11:20:02

Java Lambda 表达式的奇幻之旅的相关文章

Java Lambda表达式入门[转]

原文链接: Start Using Java Lambda Expressions http://blog.csdn.net/renfufei/article/details/24600507 下载示例程序 Examples.zip .原文日期: 2014年4月16日 翻译日期: 2014年4月27日翻译人员: 铁锚简介(译者注:虽然看着很先进,其实Lambda表达式的本质只是一个"语法糖",由编译器推断并帮你转换包装为常规的代码,因此你可以使用更少的代码来实现同样的功能.本人建议不要

Java Lambda表达式入门

原文链接: Start Using Java Lambda Expressions 下载示例程序 Examples.zip . 原文日期: 2014年4月26日 翻译日期: 2014年4月27日 翻译人员: 铁锚 简介 (译者注:虽然看着很先进,其实Lambda表达式的本质只是一个"语法糖",由编译器推断并帮你转换包装为常规的代码,因此你可以使用更少的代码来实现同样的功能.本人建议不要乱用,因为这就和某些很高级的黑客写的代码一样,简洁,难懂,难以调试,维护人员想骂娘.) Lambda表

Java Lambda表达式forEach无法跳出循环的解决思路

Java Lambda表达式forEach无法跳出循环的解决思路 如果你使用过forEach方法来遍历集合,你会发现在lambda表达式中的return并不会终止循环,这是由于lambda的底层实现导致的,看下面的例子: public static void main(String[] args) { List<String> list = Lists.newArrayList(); list.add("a"); list.add("b"); list.

使用Java lambda表达式实现Flink WordCount

本篇我们将使用Java语言来实现Flink的单词统计. 代码开发 环境准备 导入Flink 1.9 pom依赖 <dependencies> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-java</artifactId> <version>1.9.0</version> </dependency> <de

java lambda表达式学习笔记

lambda是函数式编程(FP,functional program),在java8中引入,而C#很早之前就有了.在java中lambda表达式是'->',在C#中是‘=>’. 杜甫说:射人先射马,擒贼先擒王.学习一个库要学习它的入口类.lambda的入口类是Stream,一看Stream中的函数就会发现Function,Predicate等lambda元素. 一.几个概念     函数式接口 Functional Interface,除了static和default类型的方法外,只有一个函数

Java Lambda 表达式的几各形式

一.Lambda表达式的组成主要有传入参数列表(参数1,参数2,...),箭头 ->,及返回值,可以是某个值,或者结构体. 如下举一个小小的例子: 在没有使用Lambda表达式时: import java.util.stream.Stream; /** * * @author Kangjun */ public class Demo { public static void main(String[] args){ Dog dog1 = new Dog("dog1",2); Do

Java Lambda表达式 实现原理分析

https://blog.csdn.net/qq_37960603/article/details/85028867 在类编译时,会生成一个私有静态方法+一个内部类. 在内部类中实现了函数式接口,在实现接口的方法中,会调用编译器生成的静态方法. 在使用lambda表达式的地方,通过传递内部类实例,来调用函数式接口方法. 原文地址:https://www.cnblogs.com/eryun/p/12149841.html

java lambda表达式

ArrayList<String> list = new ArrayList<String>(); list.add(0, "b"); list.add(1, "a"); list.add(0, "c"); list.add(1, "d"); ITopable<String> sortDesc = ((strList) -> { String tmp = null; for (Stri

【Java学习笔记之三十一】详解Java8 lambda表达式

Java 8 发布日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Java 8之前,如果想将行为传入函数,仅有的选择就是匿名类,需要6行代码.而定义行为最重要的那行代码,却混在中间不够突出.Lambda表达式取代了匿名类,取消了模板,允许用函数式风格编写代码.这样有时可读性更好,表达更清晰.在Java生态系统中,函数式表达与对面向对象的全面支持是个激动人心的进步.将进一步促进并行