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编译器会抛出一个错误,并让开发人员来解决这个冲突,而不会自动选择的其中一个的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值