再来看看Java8的新特征lambda表达式

36次阅读

共计 2450 个字符,预计需要花费 7 分钟才能阅读完成。

什么是 lambda 表达式?

可以把 Lambda 表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。

比如说 new 一个 Thread 的传统写法如下

Thread t = new Thread(new Runnable() {public void run(){System.out.println("Hello world");
 }
}); 

那么利用 lambda 表达式的写法就是

Thread t = new Thread(() -> System.out.println("Hello world")); 

->左边的就是参数列表,->右边的就是函数主体

函数式接口

为什么 @FunctionalInterface 注解修饰的类只能有一个抽象函数

查看 Java8 的源码,被 @FunctionalInterface 修饰的函数叫做函数式接口,例如 Predicate,这些类往往只有一个抽象函数,那是因为“Lambda 表达式理解为简洁地表示可传递的 匿名函数 ”,直接使用的匿名函数的时候没有指定函数名称,所以,如果有两个及以上抽象函数的时候,虚拟机就不知道你要执行哪个方法了,如上例中 Runnable 的 run() 方法,我们参数列表部分只使用了(),并没有声明调用的函数名。

JDK 自带的函数式接口都在 java.util.function 路径下,常用的有

public interface Predicate<T>{boolean test (T t);
} 
public interface Consumer<T> {void accept(T t);
}
public interface Function<T, R> {R apply(T t);
}
...

函数式接口使用示例

// 源码部分
@FunctionalInterface
public interface Predicate<T>{boolean test(T t);
}
// 方法构建
public static <T> List<T> filter(List<T> list, Predicate<T> p) {List<T> results = new ArrayList<>();
 for(T s: list){if(p.test(s)){results.add(s);
 }
 }
 return results;
}
// 使用示例,通过 filter 方法,筛选出 String 不为空的数据
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate); 

其他函数式接口使用示例

为什么 lambda 表达式使用局部变量必须是 final 的?

lambda 表达式主体部分除了使用参数列表的数据,还可以使用 lambda 表达式外部的局部变量,但是这些局部变量只能声明一次,否则就会报错。

int portNumber = 1337;
// 此时会报错,portNumber 必须被 final 修饰
Runnable r = () -> System.out.println(portNumber);
portNumber = 31337; 

因为 lambda 表达式主体可看作是匿名内部类,访问外部局部变量是需要 final 的。从线程的角度来说,就是局部变量是一个线程(假设叫线程 A),lambda 表达式主体是另外一个线程(线程 B),当线程 A 结束的时候,线程 B 还要访问线程 A 的数据,肯定是不行的,所以线程 B 中的变量实质上不是指向线程 A 中的变量,而是拷贝了一份出来,所以必须保证拷贝出来的数据是不可以改变的。

方法引用

lambda 表达式还有一个非常方便的地方,就是方法引用,可以通过 类名:: 方法名 的形式直接使用方法。

例如

// 静态方法
Integer::parseInt
// 对象的普通方法
String::length
// 构造方法
Apple::new

复合 lambda 表达式的用法

lambda 表达式还可以链式调用,同时拥有与或非 (negate、and 和 or) 的逻辑判断

// 链式调用
inventory.sort(comparing(Apple::getWeight)
 .reversed()
 .thenComparing(Apple::getCountry));
 
// 非
Predicate<Apple> notRedApple = redApple.negate();
// 与
Predicate<Apple> redAndHeavyApple =
 redApple.and(a -> a.getWeight() > 150);
// 或
Predicate<Apple> redAndHeavyAppleOrGreen =
 redApple.and(a -> a.getWeight() > 150)
 .or(a -> "green".equals(a.getColor())); 

函数复合

Function 函数接口提供了两个方法对数据做 连续操作,andThen 和 compose 方法。

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g);
int result = h.apply(1);
// 输出 3 ==> )(1*2)+1

andThen 方法相当于先执行 f 函数,再执行 g 函数。

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.compose(g);
int result = h.apply(1); 
// 输出 4 ==> (1+1)*2

compose 方法相当于先执行 g 函数,再执行 f 函数。

接下来

接下来会梳理流的相关知识点、和其他(注入 Optionnal、新的时间工具、默认方法等知识)。

正文完
 0