Java 8 新特性详解(超详细)

1. Lambda表达式和函数式接口

函数式接口 FunctionalInterface

  Java8当中新增了一个特殊的注解 @FunctionalInterface, 如果一个接口被注解@FunctionalInterface 标明的话,那么这个接口就可以被称之为函数是接口。一个函数式接口只能有一个抽象方法(但是可以有多个非抽象的方法)。如果一个接口定义的抽象方法是覆盖重写自Object或者其他地方,则这个抽象方法不能为函数式接口的抽象方法数量加1,也就是说如果接口同时拥有一个自己的抽象方法以及一个或者多个从Object重写的抽象方法,这个接口仍然被称之为函数式接口。
注意:

  • 函数式接口的实例可以被方法引用,构造函数引用,以及 Lambda表示式 创建
  • @FunctionalInterface只能声明接口类型,不能使用在注解,枚举和类上(annotation type, enum, or class)
  • 如果一个接口满足函数式接口的定义,只有一个抽象的方法,那么即使没有声明@FunctionalInterface注解,编译器依然会将这个接口当成函数式接口

举个栗子:

@FunctionalInterface
public interface TestInterface {
	// abstract method
	public void sub();
	//Override the method in java.lang.Object not abstract method.
	public boolean equals(Object var1);
	// default method is not abstract method
	public default void defaultMethod(){
	}
	// static method is not abstract method.
	public static void staticMethod(){}
}

JDK 8之前已有的函数式接口

  • java.lang.Runnable
  • List item
  • List item
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.util.Comparator
  • java.io.FileFilter
  • java.nio.file.PathMatcher
  • java.lang.reflect.InvocationHandler
  • java.beans.PropertyChangeListener
  • java.awt.event.ActionListener
  • javax.swing.event.ChangeListener

JDK8 新增的函数式接口都在 java.util.function 包下,常见如下

Functional interfaceFunction descriptorPrimitive type specializationstwo-arity specialization
Predicate<T>传入一个参数,返回一个bool结果, 方法为boolean test(T t)IntPredicate, LongPredicate, DoublePredicateBiPredicate<T,U>
Consumer<T>传入一个参数,无返回值,纯消费。 方法为void accept(T t)IntConsumer, LongConsumer, DoubleConsumerBiConsumer<T,U>
Function<T,R>传入一个参数,返回一个结果,方法为R apply(T t)IntFunction<R>, IntToDoubleFunction, IntToLongFunction,LongFunction<R>,LongToDoubleFunction,LongToIntFunction, DoubleFunction<R>, ToIntFunction<T>,ToDoubleFunction<T>, ToLongFunction<T>BiFunction<T,U,R>
Supplier<T>无参数传入,返回一个结果,方法为T get()BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier
UnaryOperator<T>一元操作符, 继承Function,传入参数的类型和返回类型相同IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator
BinaryOperator<T>二元操作符, 传入的两个参数的类型和返回类型相同, 继承BiFunctionIntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator

讲了这么多,我们自己写一个函数式接口试一下

public class CustomFunctionInterface {
	
	public static String reverse(TestFunc sf, String s) {
        return sf.func(s);
    }

	public static void main(String[] args) {
		String str = "Custom Function Interface";
        //anonymous inner class	
		String result = reverse(new TestFunc() {			
			@Override
			public String func(String s) {
				return MyStrOps.strReverse(s);
			}
		}, str);
		//lambda
		String result2 = reverse(s -> MyStrOps.strReverse(s), str);
		
        System.out.println("Original string: " + str);
        System.out.println("String reserved: " + result);
        System.out.println("String reserved: " + result2);
	}
}

@FunctionalInterface
interface TestFunc{
	String func(String s);
}

class MyStrOps{
	public static String strReverse(String str) {
		String result = "";
        for (int i = str.length() - 1; i >= 0; i--) {
            result += str.charAt(i);
        }
        return result;		
	}
}

Lambda表达式

  Lambda表达式(也称为闭包)是Java 8中最大和最令人期待的语言改变。它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理。
  Lambda表达式为Java添加了了缺失的函数式编程特性。在一些函数式编程语言中,Lambda表达式的类型是函数。但在Java中,Lambda表达式是对象,他们必须依附于一类特别的对象类型—函数式接口(functional interface)
  lambda表达式是函数式编程的核心,lambda表达式即匿名函数,是一段没有函数名的函数体,可以作为参数直接传递给相关的调用者。lambda表达式极大的增加了Java语言的表达能力。lambda的语法结构为:

(parameters) -> expression
或
(parameters) ->{ statements; }
  • 可选类型声明 - 无需声明参数的类型。编译器可以从该参数的值推断。
  • 可选圆括号参数 - 无需在括号中声明参数。对于多个参数,括号是必需的。
  • 可选大括号 - 表达式主体没有必要使用大括号,如果主体中含有一个单独的语句。
  • 可选return关键字 - 编译器会自动返回值,如果主体有一个表达式返回的值。花括号是必需的,以表明表达式返回一个值。

  Java8之前我们,我们要实现往方法里面传递一段代码当数据处理,只能使用匿名内部类,但是现在Lambda为我们提供了非常简单但功能强大的函数式编程的方式。
  以list为例,Java8 新增了forEach()的方法,我们可以使用匿名内部类来处理,同时,由于Consumer<T>是一个函数式接口,我们也可以使用lambda表达式来实现遍历。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
list.forEach(new Consumer<Integer>() {
	@Override
	public void accept(Integer i) {
		System.out.println("---" + i);
	}
});
                 
list.forEach(i -> {
	System.out.println("--" + i);
});

注意: 这里的lambda表达式i -> {System.out.println("--" + i);}其实就是函数式接口Consumer的实例 Consumer consumer = i -> {System.out.println("--" + i);};

更多栗子:

List<Person> list= new ArrayList<>();
list.add(new Person("person1", 22));
list.add(new Person("person2", 33));
list.add(new Person("person3", 16));

//Comparator before java8
Comparator<Person> byAge = new Comparator<Person>() {
	@Override
	public int compare(Person o1, Person o2) {
		return o1.getAge()- o2.getAge();
	}
};
//Comparator in java8 using Lambda
Comparator<Person> byAge2 = (o1,o2) -> o1.getAge()- o2.getAge();

Comparator<Person> byAge3 = (o1,o2) ->{
	return o1.getAge()- o2.getAge();
};

Comparator<Person> byAge4 = Comparator.comparing(a->a.getAge());

Collections.sort(list,byAge4);
list.forEach(person -> System.out.println(person.getUsername()));

接口的默认方法和静态方法

  Java 8使用两个新概念扩展了接口的含义:默认方法和静态方法。我们可以在接口中定义任何静态方法和默认方法,默认方法使用 default 关键字修饰。默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要。接口提供的默认方法会被接口的实现类继承或者覆写。详细了解可以参考Java文档
例子:

public interface TimeClient {
    void setTime(int hour, int minute, int second);
    void setDate(int day, int month, int year);
    void setDateAndTime(int day, int month, int year,
                               int hour, int minute, int second);
    LocalDateTime getLocalDateTime();
    //static method
    static ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }
    // default method
    default ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }
}

方法引用

  方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法,它是lambda表达式的语法糖,可以用来代替单个lambda表达式,使语言的构造更紧凑简洁,减少冗余代码。
先看官网的一个例子:

public class Person {
    public enum Sex {
        MALE, FEMALE
    }
    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }
    public Calendar getBirthday() {
        return birthday;
    }    
    public static int compareByAge(Person a, Person b) {
        return a.birthday.compareTo(b.birthday);
    }
}
Person[] rosterAsArray = roster.toArray(new Person[roster.size()]);
Arrays.sort(rosterAsArray,
    (a, b) -> Person.compareByAge(a, b)
);
//you can use a method reference instead of a lambda expression
Arrays.sort(rosterAsArray, Person::compareByAge);

分类:

  • 静态方法引用
    语法是Class::static_method。这个方法接受一个Class类型的参数
  • 特定类的任意对象的方法引用
    语法是Class::method。这个方法没有参数
  • 构造器引用
    语法是Class::new。请注意构造器没有参数。比如HashSet::new.下面详细实例中会有。
  • 特定对象的方法引用
    语法是instance::method。这个方法接受一个instance对应的Class类型的参数

静态方法引用

Arrays.sort(rosterAsArray,
    (a, b) -> Person.compareByAge(a, b)
);
//use a method reference instead of a lambda expression
Arrays.sort(rosterAsArray, Person::compareByAge);

特定类的任意对象的方法引用

String[] stringArray = { "Barbara", "James", "Mary", "John",
    "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, (s1, s2) -> s1.compareToIgnoreCase(s2));
//use a method reference instead of a lambda expression
Arrays.sort(stringArray, String::compareToIgnoreCase);

构造器引用

public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>>
    DEST transferElements(
        SOURCE sourceCollection,
        Supplier<DEST> collectionFactory) {
        
        DEST result = collectionFactory.get();
        for (T t : sourceCollection) {
            result.add(t);
        }
        return result;
}
//lambda
Set<Person> rosterSetLambda = transferElements(roster, () -> { return new HashSet<>(); });
//method reference
Set<Person> rosterSet = transferElements(roster, HashSet::new);

特定对象的方法引用

class ComparisonProvider {
    public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
    }
        
    public int compareByAge(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
//lambda
Arrays.sort(rosterAsArray, (a, b) -> myComparisonProvider.compareByName(a,b));
//method reference
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);

JDK 常用函数式接口详解

Function

Function 接口的源码如下:

@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
 
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

Function接口除了最重要的apply方法外,还有两个默认方法 compose 和 addThen

  • apply方法
    接受一个参数,并带有返回值。执行apply方法,相当于执行lambda表达式,apply方法接收的参数就是lambda表达式的参数,apply的返回值即是lambda表达式的返回值。

  • compose方法
    这个方法接收一个function作为参数,将参数function执行的结果作为参数给调用的function,并返回一个Function,以此来实现两个function组合的功能。什么意思呢?看下面的例子:

public class FunctionTest {

    public static void main(String[] args) {
        FunctionTest test = new FunctionTest();
        System.out.println(test.compute(2, value -> value * 3, value -> value * value)); // 12

    }

    public int compute(int a, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {
        return function1.compose(function2).apply(a);
    }
}

上面的例子中,我们将function2,也就是 value -> value * value 作为参数传给 function1 的compose方法,按照compose方法的定义,我们先执行function2,function2的参数是2,所以function2执行后返回的结果是4,然后将4作为参数传给function1,也就是执行 value -> value * 3, 并返回执行结果12。

  • addThen方法
    了解了compose方法,我们再来看andThen方法就好理解了,名字的意思就是“接下来”,andThen方法也是接收一个function作为参数,与compose不同的是,先执行本身的apply方法,将执行的结果作为参数给参数中的function。也就是说andThen方法和compose方法只是参数fanction的执行顺序不一样。看下面例子,很容易就知道执行的结果是36。
public class FunctionTest {

    public static void main(String[] args) {
        FunctionTest test = new FunctionTest();
     
        System.out.println(test.compute(2, value -> value * 3, value -> value * value)); // 36

    }

    public int compute(int a, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {
        return function1.andThen(function2).apply(a);
    }  
}
BiFunction

  Function只能接收一个参数,如果需要传递两个参数就可以用到 BiFunction,BiFunction 接受两个参数并生成结果的函数,是Function的两个参数的特殊化,两个接口本身并没有什么关系。可以看到BiFunction 源码如下:

@FunctionalInterface
public interface BiFunction<T, U, R> {

    R apply(T t, U u);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}

  看到这里 BiFunction 的源码,我就有一个疑问,为什么 Function 接口有 compose 方法,而 BiFunction 接口木有呢?仔细想想,如果有compose方法的话,那就是先执行compose方法的参数 BiFunction 的apply方法,但是执行完毕后只返回一个值作为参数,而接下来要执行的 BiFunction 的apply方法需要两个参数,显然不行。同理,BiFunction 的 addThen 方法的参数类型是Function 而不是 BiFunction 是因为 apply(t, u) 的返回值只能最为一个参数。
   BiFunction 的用法和 Function 类似,只是参数由一个变成了两个。例子:

public class FunctionTest {

    public static void main(String[] args) {
        FunctionTest test = new FunctionTest();

        System.out.println(test.compute1(1, 2, (value1, value2) -> value1 + value2)); //3

        System.out.println(test.compute2(2, 3, (value1, value2) -> value1 + value2, value -> value * value)); //25

    }

    public int compute1(int a, int b, BiFunction<Integer, Integer, Integer> biFunction) {
        return biFunction.apply(a, b);
    }

    public int compute2(int a, int b, BiFunction<Integer, Integer, Integer> biFunction,
                        Function<Integer, Integer> function) {
        return biFunction.andThen(function).apply(a, b);
    }
}

Predicate

  函数式接口 Predicate 的主要的方法是test方法,接受一个参数返回一个布尔类型,主要是用于判断的时候使用,只有执行了test方法,才会真正的去执行lambda表达式。Java8 中 Predicate在 stream api 中进行一些判断的时候非常常用。
Predicate接口的源码如下:

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

和Function接口类似,Predicate 接口除了test方法外,还有三个默认方法 and, or 和 negate 方法,以及一个静态方法 isEqual
顾名思义,and, or, 以及 negate 方法就是求逻辑与,逻辑或以及逻辑非的方法。而 isEqual 方法是用来判断两个参数是否相同。
接下来看一个小栗子

public class PredicateTest {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        PredicateTest predicateTest = new PredicateTest();
        //输出偶数
        List<Integer> result = predicateTest.conditionFilter(list, item -> item % 2 == 0);
        result.forEach(System.out::println);
        System.out.println("-------");
        //输出奇数
        result = predicateTest.conditionFilter(list, item -> item % 2 != 0);
        result.forEach(System.out::println);
        System.out.println("-------");
        //输出大于5的数字
        result = predicateTest.conditionFilter(list, item -> item > 5);
        result.forEach(System.out::println);
        System.out.println("-------");
        //输出所有数字
        result = predicateTest.conditionFilter(list, integer -> true);
        result.forEach(System.out::println);
        System.out.println("-------");

		//输出 小于5 和 大于5的奇数,即 1,2,3,4,5,7,9
		predicateTest.conditionFilter2(list, item -> item > 5, item -> item % 2 == 0);
        System.out.println("---------");

		//输出false, 两个Date参数不相等
        System.out.println(predicateTest.isEqual(new Date()).test(new Date()));
    }
    
    public List<Integer> conditionFilter(List<Integer> list, Predicate<Integer> predicate){
        return list.stream().filter(predicate).collect(Collectors.toList());
    }
    public void conditionFilter2(List<Integer> list, Predicate<Integer> predicate,
                                 Predicate<Integer> predicate2) {
        for(Integer integer : list) {
            if(predicate.and(predicate2).negate().test(integer)) {
                System.out.println(integer);
            }
        }
    }
    public Predicate<Date> isEqual(Object object) {
        return Predicate.isEqual(object);
    }
}

Supplier

Supplier 接口的源码很简单,就只有一个get 方法, 不接受任何参数,返回一个结果。Supplier 的意思是供应商,供应者的意思。

@FunctionalInterface
public interface Supplier<T> {

    T get();
}

使用例子

public class SupplierTest {

    public static void main(String[] args) {
        Supplier<String> supplier = () -> "hello world";
        System.out.println(supplier.get());

		Supplier<Student> supplier = () -> new Student();
        System.out.println(supplier.get().getName());

        System.out.println("-------");

        Supplier<Student> supplier2 = Student::new;
        System.out.println(supplier2.get().getName());
    }
}

Consumer

Consumer 的意思是消费者,和Supplier(供应商)是相对的,Consumer 的accept 方法接受一个参数,没有返回值。

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

类似于Function接口,Consumer的accept只接受一个参数,但是jdk8又提供了一个BiConsumer接口类,该类与Consumer的区别是可以接受2个参数。Consumer 中还有一个default 方法 andThen, 由源码可知,这个方法是先执行自己本身的lambda表达式,然后在执行参数中的lambda表达式,所以方法名也叫andThen。
Cunsumer小例子:

public class ConsumerTest {

    public void test(Consumer<Integer> consumer) {
        consumer.accept(100);
    }
    public void testAndThen(StringBuilder s, Consumer<StringBuilder> consumer, 
    			Consumer<StringBuilder> consumer2) {

        consumer.andThen(consumer2).accept(s);
    }

    public static void main(String[] args) {
        ConsumerTest consumerTest = new ConsumerTest();

        Consumer<Integer> consumer = i -> System.out.println(i);

		// 输出 100
        consumerTest.test(consumer);
        consumerTest.test(consumer::accept);
		
		//输出 Hello world
        consumerTest.testAndThen(new StringBuilder("Hello"), sb -> sb.append(" world"), 
        			sb -> System.out.println(sb));
    }
}

在jdk内对Consumer的一个很常用的地方就是foreach方法(在 java.lang.Iterable内)

default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

方法接收一个Consumer对象,对this集合执行循环操作。
------------------------------------------------------------------------------------------------
JDK8 中还有很多函数式接口,可以根据以上的函数式接口的用法为参考,了解其他的函数式接口。我们都可以通过函数式接口的参数和返回值来猜测它大致的用法。

Optional

Optional不是一个函数式接口,而是一个精巧的工具接口,是一个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测,防止NullPointerEception产生。
下面是 Optional 源码中几个重要的方法和变量。

public final class Optional<T> {
  
    private static final Optional<?> EMPTY = new Optional<>();

    private final T value;

    private Optional() {
        this.value = null;
    }

    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

    public boolean isPresent() {
        return value != null;
    }

    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }

    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

    public T orElse(T other) {
        return value != null ? value : other;
    }

    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }

    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }
}
  1. 我们可以看到 Optional 的构造方法是 private 的, 所以 Optional 没有可访问的构造函数,而是通过工厂方法实例化,这些方法不会对返回的实例的身份做出承诺。

  2. 我们可以通过 of, ofNullable, empty 方法实例化 Optional 接口。
    of方法: 通过工厂方法非Null值创建Optional实例,需要注意的是传入的参数不能为null,否则抛出NullPointerException。
    ofNullable方法:为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional。可为空的Optional。
    empty方法: 创建一个空的Optional。

    // 传入非空字符串“hello”
    Optional<String> optional = Optional.of("hello");
    // 传入参数为null,抛出NullPointerException.
    Optional<String> optional2 = Optional.of(null);
    
    Optional<String> optional3 = Optional.ofNullable("hello");
    
    Optional<String> optional4 = Optional.empty();	
    
  3. ifPresent 用来判断值存不存在,为不为空。名为 ifPresent 的方法有两个,一个是boolean isPresent(),另一个是void ifPresent(Consumer<? super T> consumer)
    第一个ifPresent,值存在返回true,否则返回false,和以前Java的非空判断的形式差不多,不推荐使用。
    第二个ifPresent 方法,传入的参数是一个函数是接口 Consumer,当value的值不为null的时候,会执行Consumer 的 accept 方法,接受一个参数(在这里接收的参数就是value),没有返回值。也就是说当Optional容器中的值不为null时,会执行表示 Consumer 接口的lambda表达式。

    Optional<String> optional = Optional.ofNullable("hello");
    //推荐的Optional使用方式, 输出 hello
    optional.ifPresent(item -> System.out.println(item)); 
    
  4. Optional 取值的方法
    get : 如果Optional有值则将其返回,否则抛出NoSuchElementException。
    orElse : 如果Optional实例有值则将其返回,否则返回orElse方法传入的参数
    orElseGet : orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。
    orElseThrow : 如果有值则将其返回,否则抛出supplier接口创建的异常。在orElseGet方法中,我们传入一个Supplier接口。然而,在orElseThrow中我们可以传入一个lambda表达式或方法,如果值不存在来抛出异常

    Optional<String> optional = Optional.ofNullable("hello");
    
    // 输出 hello
    System.out.println(optional.get());
    
    // 输出 hello, 如果optional的值为null,则输出 world
    System.out.println(optional.orElse("world"));
    
    // 输出 hello, 如果optional的值为null,则输出 Or Else Get
    System.out.println(optional.orElseGet(() -> "Or Else Get"));
    
    // 如果optional的值为null,则抛出异常
    optional.orElseThrow(NullPointerException::new);
    
  5. map 和 flatMap
    map 函数:如果存在值,则对其执行调用mapping函数得到返回值,否则返回空Optional。map方法传入了一组实现了Function接口的lambda表达式作为参数,然后对lambda表达式的返回结果进行包装,生成Optional返回。顾名思义,map 函数是用来做mapping,所以传入的是Function接口(一个输入一个输出)。
    flatMap函数:和 map 函数的功能一样,看起来函数的实现也很相似,但是仔细看就会发现区别。map 函数的参数是 Function<? super T, ? extends U> mapper,flatMap 函数的参数是 Function<? super T, Optional<U>> mapper,这里可以看到flatMap函数传入的lambda表达式执行后的返回值的类型是Optional,所以可以看到flatMap方法源码中并没有对Function接口的apply方法的执行结果用Optional进行包装。

       Employee employee = new Employee();
       employee.setName("zhangsan");
    
       Employee employee2 = new Employee();
       employee2.setName("lisi");
    
       Company company = new Company();
       company.setName("company1");
    
       List<Employee> employees = Arrays.asList(employee, employee2);
       company.setEmployees(employees);
    
       Optional<Company> optional = Optional.ofNullable(company);
       //将	Company 转成 Employee list
       System.out.println(optional.map(theCompany -> theCompany.getEmployees()).
               orElse(Collections.emptyList()));
       //将	Company 转成 Employee list
       System.out.println(optional.flatMap(theCompany -> Optional.ofNullable(theCompany.getEmployees())).
       		orElse(Collections.emptyList()));
    

未完待续。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值