Java基础之Lambda表达式

77 阅读3分钟

上一篇文章中讲述了Java内部类,这篇文章主要说明的是Lambda表达式,Lambda初听感觉很高大上,因为我是做前端的所以感觉Lambda和JavaScript的箭头函数差不多,但不是完全相同的,可能只是在语法上有某些相同之处。

Lambda语法上主要分为三个部分,函数列表,箭头和方法体三个部分组成。Lambda表达式在使用的时候和匿名内部类是有相似之处的。

public interface Sum {
    int sum(int b, int... ints);
}
Sum sum = new Sum() {
    @Override
    public int sum(int... ints) {
        return 0;
    }
};

上述代码中则是匿名内部类的实现方式,实际上Lambda表达式并不仅仅是匿名内部类的语法糖,JVM内部是通过invokedynamic指令来实现Lambda表达式的。但Lambda表达式并不能取代所有的匿名内部类,只能用来取代函数接口(Functional Interface)的简写。

public interface Sum {
    int sum(int b, int... ints);
}
Sum sum = (b, nums) -> {
    int result = 0;
    for(int num : nums){
        result += num;
    };
    return b + result;
};
int result = sum.sum(1000,1,2,3,4,5,6,7,8,9);
System.out.println(result);

上述代码中使用Lambda方式实现了Sum,并通过实例调用到sum方法。将上下两端代码进行对比,其实Lambda和匿名内部类的作用是一样的,但比匿名内部类更进一步。

Lambda可以精简写法,这一点和 javascript的箭头函数的特性是一致的。

// 有参简写
Container container = str -> Integer.parseInt(str);
int result = container.conver("123456");
System.out.println(result);
// 无参简写
Container container = () -> Integer.parseInt(str);
int result = container.conver("123456");
System.out.println(result);

无论是匿名内部类和Lambda表达式都离不开interface接口,当决定使用Lambda表达式去实现接口的时候,interface中只能包含一个方法。但是在项目中可能会有其他地方使用同一个interface,如果不小心在同一个interface中添加了一个方法,很容易导致整个Java程序编译报错。为了解决这个问题Java提供了FunctionalInterface注解,用来限定该接口只能用于Lambda表达式。

@FunctionalInterface
public interface Sum {
    int sum(int b, int... ints);
}

Lambda表达式在实现方法的时候,可以使用当前Lambda表达式作用域内的所有变量,不仅限于Lambda表达式中所传如的参数(但是,一般不会这样写,这样强耦合的代码不利于维护)。

Lambda表达式除了自己去实现之外还是引用已有类型中的方法,所谓方法引用,是指如果某个方法签名和接口恰好一致,就可以直接传入方法引用。

@FunctionalInterface
public interface Container {
    Integer conver(String str);
}
Container container = Integer::parseInt;
int result1 = container.conver("123456");
System.out.println(result1);

这是什么情况呢?方法引用一定要保证接口的参数和返回值是一致的,如上述代码中,parseInt方法所接收的参数和返回值与Container.conver参数和返回值完全一致我们可以直接引用parseInt的方法。我们再看下下面这段代码:

@FunctionalInterface
public interface FuncTest {
    Map<String, String> createMap();
}
FuncTest funcTest = () -> {
    return new HashMap<>();
};
Map<String, String> map = funcTest.createMap();
System.out.println(map);

FuncTest接口中有一个createMap方法,并使用Lambda表达式去实现了创建Map的方法。这种情况下一般可以直接引用new来实现,这样看来是不是就清晰很多了呢。

FuncTest funcTest = HashMap::new;
Map<String, String> map = funcTest.createMap();
System.out.println(map);

再看下面这段代码,StrOption的接口要求实现subStr,仔细观察一下会发现在实现Lambda表达式的时候,引用了String的substring方法,但是substring在调用的时候确只需要两个参数开始索引和结束索引。这种情况Java会默认将第一个参数作为其调用方法的调用者去执行引入方法

@FunctionalInterface
public interface StrOption {
    String subStr(String str, int begin, int end);
}
StrOption strOption = String::substring;
String result2 = strOption.subStr("零何一",2, 3);
System.out.println(result2);

本篇文章把Lambda表达式的基础用法大致上梳理了一下,至少对于Lambda表达式能有一个简单的基础认知,虽然只是简单的使用在其中也学到了不少的东西。