1、Java8 Lambda表达式
Lambda表达式也称为闭包,它允许我们把函数当作参数一样传递给某个方法,或者把代码本身当作数据处理。
早期Java开发者只能使用匿名内部类来实现Lambda表达式。
最简单的可以由逗号分隔的参数列表、->
符号、语句块三部分组成。
例如:
// 例子1
// 参数e的类型是编译器推理出来的
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );
// 例子2
// 当然也可以将执行参数的类型写上
Arrays.asList( "a", "b", "d" ).forEach((String e)-> System.out.println( e ) );
// 例子3
// 当有多个参数时
Arrays.asList( "a", "b", "d" ).sort((e1,e2)-> e1.compareTo(e2));
// 例子4
// 当Lambda的语句块只有一行时,可以不使用return语句。
Arrays.asList( "a", "b", "d" ).sort((e1,e2)-> e1.compareTo(e2));
ps: 切记当有多个参数,或需要指定参数类型的时候,参数列表要加括号。
2、 函数式接口
函数式接口(Functional Interface
)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
作用: 这样的接口可以隐式转换为Lambda表达式。
只要某个开发者在该接口中添加一个函数,则该接口就不再是函数式接口,进而导致编译失败。为了客服这种问题,并显式说明某个接口是函数式接口,Java8提供了一个特殊的注解**@FunctionalInterface**Java 库中的所有相关接口都已经带有这个注解了。
@FunctionalInterface
interface Addtions {
int test(int a, int b);// 我是核心
default void hello() {
System.out.println("我不会影响到函数式接口的定义");
}
static void hello1(){
System.out.println("我也不会影响到函数式接口的定义");
}
}
个人觉得常用的几个接口:
- java.util.function.Function
R apply(T t);
- java.util.function.Supplier
T get();
- java.util.function.Predicate
boolean test(T t);
- java.util.function.Consumer
void accept(T t);
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.lang.reflect.InvocationHandler
写lamdba表达式时会经常用到四个标黑的函数式接口,重点是她们方法的返回值和方法参数。
3、接口的默认方法和静态方法
Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default
关键字即可,这个特征又叫做扩展方法,示例如下:
- 默认方法可以被实现类重写Override
class FunctionalInterfaceTest implements Formula{
@Override
public double calculate(int a) {
return 0;
}
// 可以重写sqrt方法。
@Override
public double sqrt(int a) {
return Formula.super.sqrt(a);
}
}
@FunctionalInterface
interface Formula {
double calculate(int a);
// 该方法(默认方法)可以被实现类重写
default double sqrt(int a) {
return Math.sqrt(a);
}
static void hello1(){
System.out.println("我是新来的(JAVA8),我叫静态方法,");
}
}
4、方法引用
方法引用使得开发者可以直接引用现存的方法、Java类的构造方法或者实例对象。方法引用和Lambda表达式配合使用,使得java类的构造方法看起来紧凑而简洁,没有很多复杂的模板代码。
可见使用Lambda表达式的写法和使用方法引用的写法的效果是一样的,但是使用方法引用有时会更加简化代码
- 构造器引用
类名::new
- 静态方法引用
类名::静态方法
- 对象方法引用
类名::方法
- 当Lambda表达式的参数列表第一个参数为实例方法的调用者,第二个参数(或无参)是实例方法的参数时,可以使用这种方法。
- 实例方法引用
实例对象::成员方法
- 要先获取一个实例对象
public class Test {
private String name;
public String getName() {
return this.name;
}
public Test(String name) {
this.name = name;
}
public static String staticMethod(){
return "我是静态方法!";
}
public static void main(String[] args) {
Test test1 = new Test("小明");
// Lambda表达式
Supplier<String> func1 = () -> test1.getName();
System.out.println("Lambda表达式测试:" + func1.get());
// 实例方法引用
Supplier<String> func2 = test1::getName;
System.out.println("方法引用方式测试:" + func2.get());
// 静态方法引用
Supplier<String> func3 = Test::staticMethod;
System.out.println("静态方法引用测试:" + func3.get());
// 构造方法引用(构造器引用)
Function<String, Test> func4 = Test::new;
Test test2 = func4.apply("xxx");
System.out.println("构造方法引用测试:" + test2);
// 对象方法引用
// Test为类名,getName为成员方法。
Function<Test, String> func5 = Test::getName;
System.out.println("对象方法测试引用:" + func5.apply(test1));
}
}
5、Optional
Java应用中最常见的bug就是NullPointerException,
就比如比较两个字符串是否相等
s1.equals(s2)
,如果s1==null
,那么一运行,console
立马就爆红了。
所以Java8提供了Optional来解决这问题。
- isPresent(): 如果Optional实例持有一个非空值,方法返回true,否则返回false
- orElseGet():,Optional实例持有null,则可以接受一个lambda表达式生成的默认值
- map(): 可以将现有的Opetional实例的值转换成新的值
- orElse(): Opetional 实例持有null的时候返回传入的默认值, 方法与orElseGet() 方法类似。
- filter(): 如果optional实例不为null,并且filter中lambda表达式返回true,就返回一个Optional实例;反之返回一个空optional。
-
If a value is present, and the value matches the given predicate,return an {@code Optional} describing the value, otherwise return an empty {@code Optional}.
-
- 当optional实例为null时
Optional< String > fullName = Optional.ofNullable( null );
System.out.println( "Full Name is set? " + fullName.isPresent() );
System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) );
System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
// 下面为输出结果
Full Name is set? false
Full Name: [none]
Hey Stranger!
- 当optional实例不为null时
Optional< String > firstName = Optional.of( "Tom" );
System.out.println( "First Name is set? " + firstName.isPresent() );
System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) );
System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ));
//输出结果
First Name is set? true
First Name: Tom
Hey Tom!
参考资料
https://baijiahao.baidu.com/s?id=1652786021461159890&wfr=spider&for=pc
https://stackoverflow.com/questions/25197066/java-8-functional-interface-assignment-context