Java 8 Lambda 表达式示例(Java 8 Lambda Expressions by Examples)

编程语言 William 273浏览 0评论

Ever since I heard Java 8 is going to support lambda expressions (aka closures), I was very enthusiastic to spice my code with such a decent and concise functional element. Most developers extensively use Anonymous Inner Classes for event handlers, comparators, basic thread/runnable implementations, etc and overburden the logic with unnecessary scaffoldings even a very simple piece of code seems complex and unwieldy. Java 8 now added Lambda expressions as part of language syntax that help solve similar problem in a very elegant manner.

It enable developers to encapsulate a single unit of behavior and pass it to other code. It’s like syntactic sugar for an anonymous class (with one method whose type is inferred) and is an object-less method. I prefer avoiding extensive theoretical material in this post, but before moving further towards understanding the syntax, structure and examples of lambdas, there is an important concept need attention.

自从我听说Java8将要支持Lambda表达式(或称闭包),我便开始狂热的想要将这些体面的简洁的功能元素应用到我的代码中来。大多开发者普遍的使用匿名内部类来开发事件处理器,比较器,thread/runnable实现等等,一些没有必要的辅助代码将逻辑复杂化,即便一些非常简单的代码也变的复杂不堪。Java8现在加入了Lambda表达式作为语法的一部分将会极大地解决这一类似问题。

它使得开发者可以封装一个单独的行为单元并且传递给其他代码。他更像是一个匿名类(带有一个方法的可推断类型)的语法糖和一个更少对象的方法。我希望这篇文章不会扯过多广泛的理论材料,但是在理解Lambda的语法,结构和实例前,这里还是有一个非常重要的概念需要注意。

Functional Interface

A Functional Interface (aka Single Abstract Method type or SMA) is any interface that contains only one abstract method. But it may contain some static or/and default methods. java.lang.Runnable is an example of a Functional Interface, as it have only one run() method, which is abstract. Similarly ActionListener interface is also a functional interface. Following is an example of user defined functional interface.

interface Worker() {

    boolean doWork();
};

Have a look at another templatized functional interface example:

interface Operator {

    TYPE operate(TYPE operand1, TYPE operand2);
}

That’s it, as it’s a normal interface that just have one abstract method. Although there is more to talk on functional interface specially java 8’s provided package
java.util.function and @FunctionalInterface annotation but for now just focus on lambdas. I will cover these topic in detail in a separate post.

函数式接口

一个函数式接口 (又或者称单抽象方法类型或者SMA)是任何包含为一个抽象方法的接口. 但它可能包含一些static或者/和default 方法.java.lang.Runnableis是一个函数式接口的例子, 因为他只有一个run()方法, 并且是抽象的. 类似的 ActionListener也是一个函数式接口. 下面是一个用户自定义函数式接口的例子。

interface Worker() {

    boolean doWork();
};

喊一下另一个典型的函数式接口的例子:

interface Operator {

    TYPE operate(TYPE operand1, TYPE operand2);
}

就是这样, 因为他就是一个含有一个抽象方法的普通接口. 尽管对于函数式接口还有很多需要讨论尤其是java8提供的 java.util.function包和@FunctionalInterface注解,但现在仅仅关注lambdas。我会在另一篇单独的博客中讨论那些话题。

Lambda Expressions

Lambda expressions, also known as closures, are anonymous methods that provide developers with a simple and compact means for representing behavior as data.

– Brian Goetz, Specification Lead for JSR 335

To easily understand the syntax of a lambda expression we first take a look at conventional Anonymous Inner Class.

new Runnable() {

    public void run() {

        performWork();
    }
};

Lambda expressions provide the remedy for clumsiness of an Anonymous Inner Class and convert above five lines into a single line like.

() -> performWork();

Syntax and Structure

So, standard syntax of lambda is as follows:

() -> some expression

Or

(arguments) -> { body just like function }

A lambda expression consists of the following three parts:

  1. A comma-separated list of formal parameters enclosed in parentheses.

    // Taking two integers and retuning their sum
    (int x, int y) -> x + y
    
    // lambda expression with single integer argument that returns its next integer value
    (int x) -> { return x + 1; }

    You can omit the datatype of the parameters in a lambda expression.

    // same lambdas without argument types
    (x, y) -> x + y
    
    (x) -> { return x + 1; }

    In addition, you can omit the parentheses if there is only one parameter.

    // single argument lambda without parentheses
    x -> { return x + 1; }
  2. The arrow token, ->

    //Lambda taking no argument and returning a constant integer vale, 92
    () -> 92
    
    // taking a string as an argument and printing that on console
    (String s) -> { System.out.println(s); }
  3. A body, which consists of a single expression or statement block. In the expression form, the body is simply evaluated and returned.

    // for single statement body, no need to use braces and the return statement
    x -> x + 1
    
    // simple lambda with void return type
    () -> System.out.println(“Hello World!”)

    In the block form, the body is evaluated like a method body and a return statement returns control to the caller of the anonymous method.

Ok, we spend enough on syntax of lambda, let’s move to some real examples

Lambda 表达式

Lambda 表达式,也被称为闭包,是为开发者提供用简单和紧凑的方式表示数据的匿名函数。

– Brian Goetz, Specification Lead for JSR 335

为了更容易的了解lambda表达式的语法,我们先来看一下常规的匿名内部类。

new Runnable() {

    public void run() {

        performWork();
    }
};

Lambda 表达式为匿名内部类提供了一个更简洁的实现方法,上面的5行代码可以被转换成下面这一行代码:

() -> performWork();

语法和结构

所以,标准的lambda表达式的语法像下面这样:

() -> some expression

或者

(arguments) -> { body just like function }

一个 lambda 表达式有以下三部分组成:

  1. 用括号包裹,并以逗号分割的参数列表

    // 接受连个整数,并返回它们的和
    (int x, int y) -> x + y
    
    // 用一个整数作为参数的lambda表达式,并且返回该整数的下一个整数
    (int x) -> { return x + 1; }

    在lambda表达式中,你可以省略参数的数据类型。

    // 同样的lambda表达式,但是没有了数据类型
    (x, y) -> x + y
    
    (x) -> { return x + 1; }

    此外,如果只有一个参数的话,你甚至可以将括号省略掉

    // 只有一个参数的lambda表达式,并且省略掉了括号
    x -> { return x + 1; }
  2. 箭头符号,->

    //没有参数,并且返回一个常数92的lambda表达式
    () -> 92
    
    // 接受一个字符串作为参数,并且将其输出在控制台中
    (String s) -> { System.out.println(s); }
  3. 函数体(body)——包括至少一句表达式,或这一个表达式块。在表达式中,主体部分被执行,并且返回。

    // 一条语句的body,没有必要使用花括号包裹或返回语句。
    x -> x + 1
    
    // 简单的lambda表达式,返回值为空
    () -> System.out.println(“Hello World!”)

    在代码块的形式中,body就像一个方法的body那样被执行,并且返回语句将流程返回到匿名方法的调用处。

好了,我们已经花了些时间来了解lambda表达式的语法,接下来,让我们来看一些实际的例子吧。

Examples of Lambda Expressions

To easily understand the lambda expressions, let’s start with some basic comparative examples with anonymous inner class. In the first example, we will see the use of lambda Comparator interface implementation. Assume we have a Person class with name property, and we constructed an array of Person objects, named persons.

Arrays.sort(persons, new Comparator() {

    @Override
    public int compare(Person first, Person second) {

        return first.getName().compareTo(second.getName());
    }
});

// it’s a standard sort but interestingly rather than passing Comparator object, it’s taking a lambda expression
Arrays.sort(persons,(first, second) -> first.getName().compareTo(second.getName()));

Notice that five lines of code turned into a single line, that’s the beauty of lambda over anonymous inner classes. Now let’s look at another example of Runnable implementation. And same is in the Runnable case.

Runnable printer = new Runnable() {

    @Override
    public void run() {

        System.out.println("Hello, I’m inside runnable class...");
    }
};

printer.run();

printer = () -> System.out.println("Hello, I’m inside runnable lambda...");

printer.run();

Writing lambda expression for user defined functional interface is also very simple and easy. Following example uses Operator custom interface and stores lambda expression in a reference variable for the sake of reusability;

Operator addition = (op1, op2) -> op1 + op2;

System.out.println("Addition result: " + addition.operate(2, 3));

For further understanding, there is another example of lambda expressions with statement block.

interface GenericOperator {

    TYPE operate(TYPE ... operands);
}

//lambda expression with statement block
GenericOperator multiply = numbers -> {

    int result = 0;

    for(int num : numbers)
        result *= num;

    return result;
};

System.out.println("Multiplication result: " + multiply.operate(2,3,4));

As demonstrated above, the block is just like a normal function block and lambdas are functional elements similar to methods, but with higher significance.

Lambda 表达式的例子

为了更容易的了解lambda表达式,让我们先来看几个那lambda表达式和匿名内部类作比较的例子。在第一个例子中,我们会看到使用lambda表达式来实现一个 Comparator 接口。假设我们有一个Person 类,该类有一个name属性,并且,我们创建了一个Person类型的数组,名为persons。

Arrays.sort(persons, new Comparator() {

    @Override
    public int compare(Person first, Person second) {

        return first.getName().compareTo(second.getName());
    }
});

// 这也是一个标准的排序,但是有趣的是,它传入的是一个lambda表达式,而不是一个Comparator类型的对象
Arrays.sort(persons,(first, second) -> first.getName().compareTo(second.getName()));

注意到5行的代码被精简到了1行,这就是lambda表达式比匿名内部类漂亮的地方。接下来,让我们看一个用lambda表达式来实现 Runable 接口的例子。结果也是一样。

Runnable printer = new Runnable() {

    @Override
    public void run() {

        System.out.println("Hello, I’m inside runnable class...");
    }
};

printer.run();

printer = () -> System.out.println("Hello, I’m inside runnable lambda...");

printer.run();

为用户自定义的接口写lambda表达式也同样是间非常简单容易的事。下面的例子使用 Operator自定义接口,并且将lambda表达式的引用保存在了变量中,以便重用。

Operator addition = (op1, op2) -> op1 + op2;

System.out.println("Addition result: " + addition.operate(2, 3));

为了更深入的理解,这里有一个使用了代码块的lambda表达式的例子。

interface GenericOperator {

    TYPE operate(TYPE ... operands);
}

//使用了代码块的lambda表达式
GenericOperator multiply = numbers -> {

    int result = 0;

    for(int num : numbers)
        result *= num;

    return result;
};

System.out.println("Multiplication result: " + multiply.operate(2,3,4));

如上所示,代码块就如同一个普通方法的代码块,lambda表达式就如同一个方法,但是它更有意义。

When to use Lambda expression

Lambdas are not actually the replacement of anonymous inner classes but a batter way to implement the single abstract method types. Both of them has their significance and are intended to use in their specific scenarios.

  • Lambda expression are meant to implement a single unit of behavior that is intended to pass to other code.

  • Its usable when just simple instance of functional interface is desired, without having a type, constructor and related stuff.

  • On the other hand anonymous inner classes are used when new fields and functionality is required.

什么时候使用Lambda表达式

Lambda表达式并不是匿名内部类的替代者,而是一个用来实现单一抽象方法的更好的方式。两者都有自己的意义,并在各自特定的情况下使用。

  • Lambda 表达式用来实现单一的行为,并且该行为要被传递个其他的代码。

  • 当只需要一个功能接口的简单实例,而不需要类型,构造方法和一些相关的东西时,Lambda表达式比较适用。

  • 另一方面,只在需要新的字段和功能时使用匿名内部类。

Lambda expressions and Anonymous inner classes

  • Anonymous classes introduce next level of scoping, whereas lambda expression are just like an enclosing environment. New variables with same name as in their super scope are allowed in classes whereas lambdas throw error and don’t allow that. Because they doesn’t introduce next level of scoping, local variable and fields/methods are directly accessible from enclosing scope.

  • Anonymous classes resolves this keyword to their own object, though lambda resolves it to enclosing class where it’s written. To access variables inside lambda from enclosing class, you can use this.

  • However, like anonymous classes, lambda expressions can only access local variables of the enclosing block that are final or effectively final. Access to non-final variables throws an error just like anonymous classes.

  • Serialization of lambda expressions are allowed only if its parameters and return type/target type are Serializable. Whereas its serialization is highly discouraged like anonymous classes.

  • Lambda expressions are compiled as private methods of enclosing class. Internally invokedynamic is used to bind methods dynamically, that were introduced in java 7.

Source Code

An IntelliJ project containing the source files for the examples covered in the post.
Download Source

Lambda表达式和匿名内部类

  • 匿名内部类会引入下一级作用域,而lambda表达式不会。在匿名内部类中可以出现和父类中相同名字的变量,但这在lambda表达式中会报错,并且不允许这样做。因为后者不会引入下一级作用域,并且父级作用域中的局部变量和方法可以被直接访问。

  • 匿名内部类把 this 关键字作为它自身的对象,而lambda表达式把它当成是该表达式所在类的对象。在表达式所在的类中,你可以通过使用 this 来访问lambda表达式中的变量。

  • 但是,像匿名内部类那样,lambda表达式只能访问所在类中用final修饰的变量。同样的,如果要访问非final修饰的变量则会报错。

  • 只有当lambda表达式的参数和返回值都是Serializable类型时才允许将其序列化。就像不推荐序列化匿名内部类一样,我们也不推荐序列化lambda表达式。

  • Lambda表达式被编译成其所在类的私有方法。java 7中引入的invokedynamic被用来动态的绑定方法。

Source Code

下面是一个IntelliJ的项目,包含了该篇文章中所有例子的源码。
下载源码


via:oschina

转载请注明:AspxHtml学习分享网 » Java 8 Lambda 表达式示例(Java 8 Lambda Expressions by Examples)

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址