《Java8新特性》上之Lambda表达式、函数式接口、方法引用、Optional

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}.

  1. 当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!
  1. 当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

https://www.jianshu.com/p/5b800057f2d8

发布了148 篇原创文章 · 获赞 52 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/m0_38072683/article/details/104783644