3.1 Java 8Lambda表达式

  Vert.x从版本3(当前最新Vert.x版本为3.5.0)开始就需要使用Java 8,并且大量使用了Lambda表达式这个Java 8的新特性。所以在真正介绍怎么使用Vert.x之前,我们需要先了解一下Java 8中的Lambda表达式

  Lambda表达式在很多语言中都有,其本质就是匿名函数:

JavaScript:

function (a, b) {  
    return a + b; 
};

Python:

lambda a,b: a + b

Lua:

function (a, b) 
    return a + b 
end

C++:

[](int a, int b) {
    return a + b;
};

C#:

(a, b) => a + b;

  Lambda表达式PythonJavaScriptLua 等弱类型语言中可以直接作为一个普通变量,在 C++ 中可以作为特定类型的函数指针(使用auto作为指针变量的类型即可自动推断类型),在 C# 中可以作为一个特定类型的委托变量,而在 Java 8 中,Lambda表达式 实际上就是一个 匿名内部类

  什么是 匿名内部类 呢?在过去版本的 Java 中,如果我们想像传递变量那样传递一个 函数 (或者叫 方法 ),通常是先定义一个 接口 ,像这样:

public interface MyFunction { 
    int exec(int a, int b);  
}

  然后,我们再实现这个接口,像这样:

MyFunction sum = new MyFunction () { 
    @Override
    public int exec(int a, int b) {
        return a + b; 
    }
};

  这个 sum 变量(或者叫对象)的 exec 就是我们要传递的 函数,我们可以通过传递 sum 变量来传递这个 函数,而这个对接口的实现就叫做 匿名内部类

  现在,在Java 8中,你可以不用修改接口定义,直接用 Lambda表达式 这样写:

MyFunction sum = (a, b) -> a + b;

  也可以这样写:

MyFunction sum = (int a, int b) -> a + b;

  或者这样写:

MyFunction sum = (a, b) -> { 
    return a + b; 
};

  当然以前的写法仍然是可以的,不过我觉得能一行搞定的事你肯定不会再用四行了。与一般的 匿名内部类 所实现的接口不同的是,配合 Lambda表达式 使用的接口(比如这里的 MyFunction)需要满足一个条件:

除静态方法和默认方法外,该接口只有一个抽象方法。

  这个条件是一个接口能用做一个Lambda表达式的类型的充分必要条件,而那个抽象方法名是什么并不重要。这样的接口也被称为 函数接口。所以,按照这个条件,Java原有的很多只有单个抽象方法的接口都可以写成Lambda表达式了,比如 Runnable,以前我们启动一个新线程运行一段代码会这样写:

new Thread(new Runnable() { 
    @Override 
    public void run() {  
        // blah...blah...  
    } 
}).start();

  而现在,我们可以这样写:

new Thread(() -> {
    // blah...blah... 
}).start();

  只要你写的Lambda表达式类型正确就可以了,下面是一个错误的例子,无法通过编译,因为 Runnablerun 方法是没有参数的,应当写一对小括号而不是一个变量名 arg

new Thread(arg -> { 
    // blah...blah... 
}).start();

  另外,Java 8还提供了注解 @FunctionalInterface ,用来加在一个 函数接口 的定义上。它会检查上面那个充分必要条件,如果不满足,比如:

@FunctionalInterface
public interface MyFunction {
    int execute(int a, int b);
    int wtf(int i);
}

  编译器将会报错:

MyFunction is not a functional interface.

  这个注解仅仅是一种约束和对调用者的提示,虽然函数接口不写这个注解仍然可以用做 Lambda表达式 的类型,但是如果这个接口的确是一个函数接口,那么写上是个比较好的习惯。大家可以看一下 Java 原有的 RunableCallableComparator 接口,在 Java 8 中都加上了 @FunctionalInterface 注解。在 Vert.x 中,通常不需要你自己定义函数接口,基本上只需要用到它定义好的 事件处理函数 (以下简称 处理函数)的接口 Handler<E>

@FunctionalInterface
public interface Handler<E> {
    void handle(E event);
}

  加上 Java 8 本身也提供了 Consumer<T>Predicate<T>Supplier<T>Function<T,R> 等很多 函数接口,我们基本不用自己定义 函数接口

  最后,再介绍一个与 Lambda表达式 相关的 Java 8 新特性——η转换(eta-conversion)——一个 函数接口 对象还可以写成下面的形式:

public class EtaConversion {

    public static void main(String[] args) {

        int a = 4;
        int b = 3;

        // sum 非静态方法需要通过实例获得
        EtaConversion obj = new EtaConversion();
        int c = ofSquare(obj::sum, a, b);

        // diff 静态方法通过类获得即可
        int d = ofSquare(EtaConversion::diff, a, b);

        // 打印计算结果
        System.out.println(a + " 和 " + b + " 的平方和为 " + c + ",平方差为 " + d + "。");
    }

    // 先对两个值分别求平方,再按 func 计算
    public static int ofSquare(MyFunction func, int a, int b) {
        return func.exec(a * a, b * b);
    }

    // 求和
    public int sum(int a, int b) {
        return a + b;
    }

    // 求差(静态方法)
    public static int diff(int a, int b) {
        return a - b;
    }
}

  以上,就是我要分享的关于 Java 8 的新特性 Lambda表达式 的所有内容。

results matching ""

    No results matching ""