1. lambda 表达式与函数式接口

直观认识一下 Lambda 表达式:

    简单的说就是一种紧凑的、传递行为的方式。也可以理解为一种匿名函数:它没有名称,但有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常的列表。

通过代码展示一下:

// 使用匿名内部类的方式
jButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Button Pressed");
    }
});
// 使用 lambda 表达式
jButton.addActionListener(e -> System.out.println("Button Pressed"));

    它们实现的是同样的功能,但是和传入一个实现某接口的对象不同,我们传入的是一段代码块——一个没有名字的函数(其实在 java 中 Lambda 表达式就是一个对象,但是在其它语言中它是一个函数)。Lambda 表达式中的 -> (箭头符号)将参数和 Lambda 表达式主体分开,主体就是要执行的动作。

为何需要 lambda 表达式?

    在 Java 7之前,我们无法将函数作为参数传递给一个方法,也无法声明返回一个函数的方法。但是在 JavaScript 中,方法参数是一个函数,返回值是另一个函数的情况是非常常见的;JavaScript 是一门非常典型的函数式语言。所以为了实现这个功能,出现了 Lambda 表达式这个概念。

引入函数式接口:

什么是函数式接口?

1. 如果一个接口只有一个抽象方法,那么该就接口就是一个函数式接口。

2. 如果我们在某个接口上声明了 FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口。

3. 如果某个接口只有一个抽象方法,但我们并没有给该接口声明 FunctionalInterface 注解,那么编译器依旧会将该接口看作是函数式接口。

通过 java 源码看看怎么定义的函数式接口:

/* ActionListener.java */
public interface ActionListener extends EventListener {
    /**
     * Invoked when an action occurs.
     * @param e the event to be processed
     */
    public void actionPerformed(ActionEvent e);
}
@FunctionalInterface
public interface Consumer<T> {
    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

    我们可以看到 Consumer<T> 接口中不仅有一个抽象方法,还有一个 default 定义的实现方法,在 java8 之前一个接口中只能有抽象方法,不能有实现方法,但是 java8 之后为了兼容它之前版本的接口定义并且能在接口中有实现方法,最后就添加使用 default 关键字来定义的实现方法(也叫默认方法)来与普通实现方法进行区分。

    另外我们还要注意,如果在一个接口中覆盖 java.lang.Object 中的方法,这个并不会在抽象方法的计数中被包含。举个例子:

// 这个是没问题的
@FunctionalInterface
public interface InterfaceTest {
    void test();
}

// 由于 FunctionalInterface 注解的接口只能有一个抽象方法,所以这个会报错
@FunctionalInterface
public interface InterfaceTest {
    void test();
    void test2();
}

// 这个也是没问题的,因为 toString() 方法是继承自 java.lang.Object 的,并不包含在该接口抽象方法的计数内,所以这个接口仍然只是有一个抽象方法
@FunctionalInterface
public interface InterfaceTest {
    void test();

    @Override
    String toString();
}

// 这个也是错误的,因为 InterfaceTest1 接口继承自 InterfaceTest 接口,但是 InterfaceTest 中已经有一个 test() 抽象方法了,再加上 test2() 方法就变成两个抽象方法了
@FunctionalInterface
public interface InterfaceTest1 extends InterfaceTest{
    void test2();
}

函数式接口和 Lambda 表达式的关系:

Note that instances of functional interfaces can be created with lambda expressions, method references, or constructor references.

通过官方文档可以知道,函数式接口的实例对象可以通过 Lambda 表达式、方法引用、构造函数引用来实现。

综上,我们可以了解到,其实 lambda 表达式在 java 中就是函数式接口的一个实现,所以 Lambda 表达式在 java 中代表的就是一个对象实例。eg:

jButton.addActionListener(e -> System.out.println("Button Pressed"));
就等价为:
ActionListener actionListener = e -> System.out.println("Button Pressed");
jButton.addActionListener(actionListener);

Lambda 表达式的几种表现形式以及如何书写:

    首先,我们知道了,所谓 Lambda 表达式其实就是函数式接口的实现,接下来看一下不同情况下,它的表达形式以及含义:

① 无参 Lambda 表达式(执行体为一行代码时)---无返回值

    Runable 函数式接口的实现(jdk 源码):

@FunctionalInterface
public interface Runnable {

    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

    可以看到抽象方法 run() 是一个无参并且无返回值的,所以就可以这样写:

Runnable noArguments = () -> System.out.println("Hello World!");

    Lambda 表达式左边参数 ()  代表无参,箭头符号右边为主体---执行体。

② 无参 Lambda 表达式(执行体为多行代码时) --- 无返回值

Runnable noArguments = () -> {
    System.out.println("Hello World!");
    System.out.println("Hello World!");
};

    Lambda 表达式的主体不仅可以是一个表达式,而且也可以是一段代码块,使用大括号 { } 将代码块括起来。该代码块和普通方法遵循的规则一样,都可以使用 return 返回或抛出异常来退出。当然一行代码中也可以使用 { }。

③ 包含一个参数的 Lambda 表达式 --- 无返回值

ActionListener 函数式接口的实现:

ActionListener oneArgument = e -> System.out.println("button clicked");

    由于 ActionListener 接口中的抽象方法参数只有一个,所以箭头符号的前面的 e 可以省略括号,并且名称也可以是任意名称。为了增加可读性,也可以给参数加上类型:

ActionListener oneArgument = (ActionEvent e) -> System.out.println("button clicked");

④ 包含两个及以上的参数且有返回值的 lambda 表达式

    函数式接口 InterfaceTest:

@FunctionalInterface
public interface InterfaceTest {
    int test(int a, int b);
}

    Lambda 表达式实现以及使用:

InterfaceTest add = (x, y) -> x + y;
int result = add.test(1, 2);
System.out.println(result);

    这里一定要知道,这个 Lambda 表达式就是 Interface 接口的实现,所以使用这个实现就可以调用相应的方法并得到结果。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值