码迷,mamicode.com
首页 > 编程语言 > 详细

Java Lambda 表达式的奇幻之旅

时间:2015-03-01 22:22:32      阅读:230      评论:0      收藏:0      [点我收藏+]

标签:java lambda   函数接口   范型   jdk 8   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.



Java Lambda 表达式的奇幻之旅

标签:java lambda   函数接口   范型   jdk 8   lambda 表达式   

原文地址:http://blog.csdn.net/technerd/article/details/44006921

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!