Java 8 Lambda表达式

Posted 爱coding的卖油翁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 8 Lambda表达式相关的知识,希望对你有一定的参考价值。

Java 8 出来很久了,正好在看RXJava,据说学习了lambda和stream api,可以能快速的理解RXJava,于是就来看看Java 8的新特性。

为什么使用 Lambda 表达式?

Lambda表达式是一个匿名函数,是一段可以传递的代码,因此它可以被执行一次或多次。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

以前我们编写一个匿名内部类:

    Runnable r = new Runnable() 

        @Override
        public void run() 
            System.out.println("hello world");
        
    ;

用lambda表达式来编写:

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

Lambda 表达式语法

Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符或剪头操作符。它将 Lambda 分为两个部分:

  • 左侧:指定了 Lambda 表达式需要的所有参数。
  • 右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。

1.语法格式一:无参,无返回值,Lambda 体只需一条语句。

Runnable r = () -> System.out.println(“hello world”);

2.语法格式二:Lambda 需要一个参数。

Consumer consumer = (arg) -> System.out.println(arg);

3.语法格式三:Lambda 只需要一个参数时,参数的小括号可以省略。

Consumer consumer = arg -> System.out.println(arg);

4.语法格式四:Lambda 需要两个参数,并且有返回值。

BinaryOperator binaryOperator = (x, y) -> return x + y; ;

5.语法格式五:当 Lambda 体只有一条语句时,return 与大括号可以省略。

BinaryOperator bo = (x, y) -> x + y;

类型推断

上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。

        BinaryOperator<String> binaryOperator = (String x, String y) -> 
            return x + y;
        ;

这其中的参数类型是可以根据上下文推断出来的,所以可以省略。写成如下代码:

        BinaryOperator<String> binaryOperator = (x, y) -> 
            return x + y;
        ;

函数式接口

只包含一个抽象方法的接口,你可以通过 Lambda 表达式来创建该接口的对象,被称为函数式接口。若 Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明。

你可以在任意函数式接口上使用 @FunctionalInterface 注解,这样做有两个好处。首先,编译器会检查标注该注解的实体,检查它是否是只包含一个抽象方法的接口,同时 javadoc 页面也会包含一条声明,说明这个接口是一个函数式接口。

package com.zhou.java8;

@FunctionalInterface
public interface MyFunction 

    public Integer getValue(Integer num);

作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。

    @Test
    public void test3() 
        Integer num = operation(100, (x) -> x * x);
        System.out.println(num);
    

    private Integer operation(Integer num, MyFunction mf) 
        return mf.getValue(num);
    

Java 内置四大核心函数式接口

其他接口

方法引用与构造器引用

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来。如下三种主要使用情况:

1.对象::实例方法

(x) -> System.out.println(x);
可以写成:
System.out::println;

2.类::静态方法

(x, y) -> Math.pow(x, y);
可以写成:
Math::pow(x, y);

3.类::实例方法

(x, y) -> x.compareToIgnoreCase(y);
可以写成:
String::compareToIgnoreCase;

注意:当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数(或无参数)时,可以用第三种 类::实例方法。

构造器引用:类名::new

与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致。

引用无参的构造器:

Supplier<String> supplier = () -> new String();
// 改进的写法
supplier = String::new;

引用有参的构造器:

Function<String, String> function = (n) -> new String(n); 
// 改进的写法
function = String::new;

接口中可以有默认的实现方法

package com.zhou.java8;

public interface Color 

    int getColor();

    default String getColorName() 
        return "red";
    

默认方法终结了以前的一种经典模式,即提供一个接口,以及实现一个接口的大多数或全部方法的抽象类,如Collection/AbstarctCollection,而现在,你只需要在接口中实现那些方法即可。

如果一个接口中定义了一个默认方法,而另一个父类或接口中又定义了同名的方法,该如何选择呢?

1.选择父类中的方法。如果一个父类提供了具体的实现方法,那么该接口中具体相同名称和参数的默认方法会被忽略。这是”类优先”规则。

2.接口冲突。如果一个父类接口提供了一个默认方法,而另一个接口也提供了一个具体相同名称和参数的方法(不管该方法是否是默认方法),那么你必须通过重写该方法来解决冲突。

package com.zhou.java8;

interface Color 

    int getColor();

    default String getName() 
        return "red";
    


interface Pan 

    void show();

    default String getName() 
        return "pan";
    


class Car implements Color, Pan 

    @Override
    public void show() 

    

    @Override
    public int getColor() 
        return 0;
    

    @Override
    public String getName() 
        return Color.super.getName();
    


当Car类实现Color和Pan接口时,除了要实现他们的抽象方法,还必须重写它们都拥有的getName()方法。否则,java编译器会抛出一个错误,并让开发人员来解决这个冲突,而不会自动选择的其中一个的。

以上是关于Java 8 Lambda表达式的主要内容,如果未能解决你的问题,请参考以下文章

Java中的Lambda表达式

死磕Lambda表达式:更简洁的Lambda

Kotlin函数 ⑤ ( 匿名函数变量类型推断 | 匿名函数参数类型自动推断 | 匿名函数又称为 Lambda 表达式 )

Java Lambda 表达式介绍

Java Lambda表达式入门

Java中Lambda表达式的使用