Java 8 早已经在2014 年 3月 18日发布,毫无疑问 Java 8 对 Java 来说绝对算得上是一次重大版本更新,它包含了十多项语言、库、工具、JVM 等方面的新特性。
比如提供了语言级的匿名函数,也就是被官方称为 lambda 的表达式语法(外界也称为闭包,lambda 的引入也让流式操作成为可能,减少了代码编写的复杂性),比如函数式接口,方法引用,重复注解。再比如 Optional 预防空指针,Stearm 流式操作,LocalDateTime 时间操作等。
为什么使用lambda表达式?
- 减少代码编写的复杂性
- 让流式操作称为可能
@Test
public void createLambda() throws InterruptedException {
// 使用 Lambda 之前
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("JDK8 之前的线程创建");
}
};
new Thread(runnable).start();
// 使用 lambda 之后
Runnable runnable1Jdk8 = () -> System.out.println("JDK8 之后的线程创建");
new Thread(runnable1Jdk8).start();
// 更加紧凑的方式
new Thread(() -> System.out.println("JDK8 之后的线程创建")).start();
}
可以发现 Java 8 中的碰到了函数接口,自动推断了要运行的方法,不仅省去了方法的编写,也代码变得更加紧凑
lambda表达式的使用
1. (params) -> expression
2. (params) -> {statements;}
- : 分割参数和处理语句
- 左边:形参列表:其实就是接口中抽象方法的形参列表
- 右边:体:重写的抽象方法体
无参数无返回值
public void test1() {
Runnable r1 = new Runnable(){
@Override
public void run() {
System.out.println("Rick");
}
};
r1.run();
System.out.println("---------------");
Runnable r2 = () -> System.out.println("Rick");
r2.run();
}
lambda需要一个参数,但是没有返回值
public void test2() {
Consumer<String> con1 = new Consumer<String>(){
@Override
public void accept(String t) {
System.out.println(t);
}
};
con1.accept("Rick");
System.out.println("-----------------------");
Consumer<String> con2 = (String t) -> {
System.out.println(t);
};
con2.accept("Rick");
}
数据类型可以省略,可由编译器推断得出
public void test3() {
Consumer<String> con3 = (t) -> {
System.out.println(t);
};
con3.accept("Rick");
}
lamdba只需要一个参数时,小括号可省略
public void test4() {
Consumer<String> con4 = t -> {
System.out.println(t);
};
con4.accept("Rick");
}
lambda需要不止一个参数,且有返回值
public void test5() {
Comparator<Integer> com = new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
System.out.println(com.compare(12, 14));
System.out.println("---------------");
Comparator<Integer> com5 = (o1, o2) -> {
return Integer.compare(o1, o2);
};
System.out.println(com5.compare(12, 14));
}
当lambda体只有一条语句时,return与大括号若有,都可省略
public void test6() {
Comparator<Integer> com6 = (o1, o2) -> Integer.compare(o1, o2);
System.out.println(com6.compare(12, 14));
}
函数式接口
Java 中的函数接口在使用时,可以隐式的转换成表达式,在Java 8中已经有很多接口已经声明为函数接口,如等。
我们来看接口:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
那么什么样子的接口才是函数接口呢?有一个很简单的定义
- 只有一个抽象函数的接口,函数接口使用注解
@FunctionalInterface
进行声明(注解声明不是必须的,如果没有注解,也是只有一个抽象函数,依旧会被认为是函数接口,并且该接口是一个编译级别的注解,用于检测函数式接口是否有错而已) - 多一个或者少一个抽象函数都不能定义为函数接口。但是,函数式接口里是可以包含默认方法、静态方法和里的方法,因为这些方法就不是抽象方法了,其有默认实现,所以是符合函数式接口的定义
- 如果使用了函数接口注解又不止一个抽象函数,那么编译器会拒绝编译。函数接口在使用时候可以隐式的转换成表达式
消费性接口Consumer:接收一个参数T,没有返回值
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
...
}
测试
@Test
public void test1() {
StringBuilder sb = new StringBuilder("Hello ");
Consumer<StringBuilder> consumer = (str) -> str.append("World!");
consumer.accept(sb);
System.out.println(sb); // Hello World!
}
供给型接口Supplier:不接受任何参数但有返回值
@FunctionalInterface
public interface Supplier<T> {
T get();
}
测试
@Test
public void test2() {
Supplier<String> supplier = () -> "Hello World!";
System.out.println(supplier.get()); // Hello World!
}
断言性接口Predicate:判断方法的参数是否符合断言
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
...
}
测试过滤字符串
@Test
public void test3() {
List<String> list = Arrays.asList("ABC", "ADR", "CDK", "ABU");
System.out.println(filterString(list, new Predicate<String>(){
@Override
public boolean test(String t) {
return t.contains("A");
}
}));
System.out.println("------------------");
// (s) -> s.contains("A")) :筛选出含有A
System.out.println(filterString(list, (s) -> s.contains("A")));
}
/**
* 过滤列表中的字符串
* @param list
* @param pr
* @return
*/
public List<String> filterString (List<String> list, Predicate<String> pr) {
List<String> res = new ArrayList<>();
for (String str : list) {
if (pr.test(str))
res.add(str);
}
return res;
}
功能性接口Function:接收一个功能参数T,并返回一个功能结果R
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
...
}
测试
@Test
public void test4() {
Function<String, String> function = (s1) -> s1 + "World!";
System.out.println(function.apply("Hello "));
}