Java8新特性-阿里云开发者社区

开发者社区> 开发与运维> 正文

Java8新特性

简介: Java8新特性

第1章 引入Lambda表达式

1.1 什么是Lambda表达式

Lambda表达式也被称为箭头函数、匿名函数、闭包;

引入的 Lambda 表达式的主要作用就是简化部分匿名内部类的写法;

能够使用 Lambda 表达式的一个重要依据是必须有相应的函数接口。所谓函数接口,是指内部有且仅有一个抽象方法的接口;

Lambda 表达式的另一个依据是类型推断机制。在上下文信息足够的情况下,编译器可以推断出参数表的类型,而不需要显式指名;

语法:() -> {}    参数列表 -> 操作表达式

1.2 为什么用Lambda表达式

1. 它不是解决未知问题的新技术
2. 对现有解决方案的语义化优化 
3. 需要根据实际需求考虑性能问题    

第2章. Lambda表达式基础知识

2.1 函数式接口

函数式接口,就是Java类型系统中只包含一个接口方法的特殊接口,Java提供了语义化检测注解@FunctionalInterface来进行检测函数式接口的合法性。

当接口中有两个接口方法时便会报错,但可以有多个静态方法和默认方法和从Object继承过来的方法。
@FunctionalInterface
public interface LambadaFunction {

    String call(String name);

    default String getAge(String name) {
        return null;
    }
    
    static String getAddress(String name) {
        return null;
    }

}

2.1.1 基本使用

对于一个函数式接口

//匿名内部类实现 实现接口的抽象方法
LambadaFunction lambadaFunction = new LambadaFunction(){
    @Override
    public String call(String name) {
        return null;
    }
};     
  
//lambda表达式实现 针对函数式接口简单实现
LambadaFunction lambadaSimple = (name) -> {
    if ("zbl".equals(name)) {
        name = "lbz";
    }
    return name;
};

2.1.2 常见的函数式接口

Jdk8提供了java.util.function包,提供了常用的函数式功能接口。

1. java.util.function.Predicate<T>

说明:接收参数对象T,返回一个boolean类型结果
//源码
@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
    //省略.....
}
//使用
Predicate<String> nameBoolean = (name) -> {
    System.out.println(name);
    return "zbl".equals(name);
};

boolean zbl = nameBoolean.test("zbl");

2.java.util.function.Consumer<T>

说明:接收一个T类型的参数,不返回任何结果。
//源码
@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
    //省略..
}    
//使用
Consumer<String>  voidConsumer = (name) -> {
    System.out.println(name);
    System.out.println("结束");
};
voidConsumer.accept("lisi");

3.java.util.function.Function<T, R>

说明:接收参数对象T,返回结果对象R
//源码
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
    //省略...
}
//使用
Function<String, List<String>> listFunction = (name) -> {
    List<String> list = Lists.newArrayList(name);
    System.out.println(list);
    return list;
};
List<String> strList = listFunction.apply("张三");

4.java.util.function.Supplier<T>

说明:不接收参数,提供T对象的创建工厂
//源码
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}
//使用
Supplier<List<String>> listSupplier = () -> {
    List<String> list = Lists.newArrayList();
    return list;
};
List<String> list = listSupplier.get();

5.java.util.function.UnaryOperator<T>

说明:接收参数对象T,返回结果对象T
//源码
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {

    /**
     * Returns a unary operator that always returns its input argument.
     *
     * @param <T> the type of the input and output of the operator
     * @return a unary operator that always returns its input argument
     */
    static <T> UnaryOperator<T> identity() {
        return t -> t;
    }
}
//使用
UnaryOperator<String> unaryOperator = (name) -> {
    return name;
};
String zhangsan = unaryOperator.apply("zhangsan");    

6.java.util.function.BinaryOperator<T>

说明:接收两个T对象,返回一个T对象结果
//源码
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
    /**
     * Returns a {@link BinaryOperator} which returns the lesser of two elements
     * according to the specified {@code Comparator}.
     *
     * @param <T> the type of the input arguments of the comparator
     * @param comparator a {@code Comparator} for comparing the two values
     * @return a {@code BinaryOperator} which returns the lesser of its operands,
     *         according to the supplied {@code Comparator}
     * @throws NullPointerException if the argument is null
     */
    public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
    }

    /**
     * Returns a {@link BinaryOperator} which returns the greater of two elements
     * according to the specified {@code Comparator}.
     *
     * @param <T> the type of the input arguments of the comparator
     * @param comparator a {@code Comparator} for comparing the two values
     * @return a {@code BinaryOperator} which returns the greater of its operands,
     *         according to the supplied {@code Comparator}
     * @throws NullPointerException if the argument is null
     */
    public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
    }
}
//使用
BinaryOperator<Integer> sum = (num1, num2) -> {
    return num1 + num2;
};
Integer apply = sum.apply(1, 3);

2.2 Lambda语法及使用

2.2.1 基本语法


    1. 声明:就是和lambda表达式绑定接口类型
    2. 参数:包含在一对圆括号中,和绑定的接口中的参数个数及顺序一致
    3. 操作符:->
    4. 执行代码块:包含在一对大括号中,出现在操作符的右侧

2.2.2 变量的访问操作

1.匿名内部类:

说明:在匿名内部类中,this代表的是匿名内部类,而不是LambdaApp这个类
public class LambdaApp {
    String s1 = "全局变量";
    
    //1.匿名内部类型中对于变量的访问
    public void testInnerClass() {
        String s2 = "局部变量";
        
        new Thread(new Runnable() {
            String s3 = "内部变量";
            
            @Override
            public void run() {
                //访问全局变量
//                System.out.println(this.s1); //this关键字表示是当前内部类型的对象
                System.out.println(s1);
                
                //访问局部变量
                System.out.println(s2); //不能对局部变量进行数据修改【final】
//                s2 = "不能修改";

                System.out.println(s3);
                System.out.println(this.s3);
            }
        });
    }

2.Lambda表达式:

说明:s1是类中的全局变量,this指代的就是类LambdaApp而不是lambda语句块
public class LambdaApp {
    String s1 = "全局变量";

    public void testLambda() {
        String s2 = "局部变量Lambda";
        
        new Thread(() -> {
            String s3 = "内部变量Lambda";
            //访问全局变量
//                System.out.println(this.s1); //this关键字表示是当前内部类型的对象
            System.out.println(this.s1);

            //访问局部变量
            System.out.println(s2); //不能对局部变量进行数据修改【final】
//                s2 = "不能修改";

            System.out.println(s3);
            s3 = "Lambda内部变量可以直接修改";
        });
    }
}    

2.3 Lambda表达式运行原理

2.3.1 lambda表达类型检查

声明函数式接口:

@FunctionalInterface
public interface MyInterface<T, R> {
    
    R strategy(T t, R r);
    
}

将MyInterface类型作为函数的参数:

public static void test(MyInterface<String, List<String>> interParam) {
    List<String> list = interParam.strategy("java", Lists.newArrayList());
    System.out.println(list);
}

lambda表达式实现该参数:

 test1((x, y) -> {
    y.add(x);
    return y;
});

lambda表达式的类型检查:

当我们将(x,y)->{..}交给test(param)参数时,JVM会推导param参数是一个MyInterface类型的参数,
所以当前的lambda表达式属于MyInterface类型,MyInterface接口就是lambda表达式的目标类型。

参数类型检查:

(x,y)->{..} -->MyInterface.strategy(T t,R r),strategy函数需要一个T类型和一个R类型,
我们在把MyInterface当作类型传递给参数时,确定了它的T类型和R类型,确定了它的T类型为String,
R类型为List,然后与lambda表达式进行推导验证,会得出(x,y)执行的就是
strategy(T t,R r)这样一个方法,所以最终推导出来x是属于T类型即String,y属于R类型即List。
总结:JVM会根据代码在运行过程中的上下文进行检测,在这里test需要一个MyInterface类型的参数,在调用test时我们传递了一个lambda表达式,
     MyInterface就是lambda表达式的目标类型,接下来会继续根据lambda表达式与绑定的接口进行类型参数的推导,在类型参数进行推导时,
     会验证lambda表达式中的参数个数与顺序是否和接口中定义的参数类型和顺序一致,一致的情况下按照参数的顺序进行确认。

2.3.2 方法重载与lambda表达式

public class LambdaApp1 {

    interface Param1 {
        void printInfo(String info);
    }

    interface Param2 {
        void printInfo(String info);
    }

    //定义重载的方法
    public void lambdaMethod(Param1 param1) {
        param1.printInfo("param1");
    }

    public void lambdaMethod(Param2 param2) {
        param2.printInfo("param2");
    }

    public static void main(String[] args) {
        LambdaApp1 app = new LambdaApp1();

        //匿名内部类实现
        app.lambdaMethod(new Param1() {
            @Override
            public void printInfo(String info) {
                System.out.println(info);
            }
        });

        app.lambdaMethod(new Param2() {
            @Override
            public void printInfo(String info) {
                System.out.println(info);
            }
        });

        //lambda表达式实现 会有问题,因为类型检查,而它不知道实现的是哪个接口
        /**
         * lambdaMethod() -> 方法 -> 重载方法
         *    -> Param1 函数式接口
         *    -> Param2 函数式接口
         *    调用方法 -> 传递Lambda表达式 -> 自动推导->
         *       -> Param1 | Param2
         * 编译错误:Error:(49, 12) java: 对lambdaMethod的引用不明确
         *   方法 lambdaMethod(com.example.testpackage1.LambdaApp1.Param1) 和 
         *   方法 lambdaMethod(com.example.testpackage1.LambdaApp1.Param2) 都匹配
         */
        app.lambdaMethod((info) -> {
            System.out.println(info);
        }) ;

    }

}

第3章 lamdba表达式高级拓展

3.1 方法引用

类型 语法 对应的Lambda表达式
静态方法引用 类名::staticMethod (args) -> 类名.staticMethod(args)
实例方法引用 inst::instMethod (args) -> inst.instMethod(args)
对象方法引用 类名::instMethod (inst,args) -> 类名.instMethod(args)
构建方法引用 类名::new (args) -> new 类名(args)

3.2 方法引用举例

3.2.1 静态方法引用

//有一个Person类
@Data
public class Person {

    private String name;
    
    private Integer age;

    public static int compareByAge(Person a, Person b) {
        return a.age.compareTo(b.age);
    }
}
//现假设,一个部门有30人,把他们存放在一个数组中,并按年龄排序,通常我们可以自己写一个比较器
Person[] rosterAsArray = new Person[30];
// 添加数组元素省略

class PersonAgeComparator implements Comparator<Person> {
    public int compare(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}
        
Arrays.sort(rosterAsArray, new PersonAgeComparator());

Arrays.sort的声明为:public static <T> void sort(T[] a, Comparator<? super T> c),比较器参数Comparator为一个函数式接口,利用上一节Lambda表达式所学知识,可以改写为以下代码:

Person[] rosterAsArray = new Person[30];
// 添加数组元素省略

Arrays.sort(rosterAsArray, (a,b) -> a.getAge().compareTo(b.getAge()));

然而,你会发现,Perdon类中已经有了一个静态方法的比较器:compareByAge,因此,我们改用Person类已经提供的比较器:

Person[] rosterAsArray = new Person[30];
// 添加数组元素省略

Arrays.sort(rosterAsArray, (a,b) -> Person.compareByAge(a,b));

以上代码,因为Lambda表达式调用了一个已经存在的静态方法,表格中的语法,上面的代码可以最终改写成静态方法引用:

Person[] rosterAsArray = new Person[30];
// 添加数组元素省略

Arrays.sort(rosterAsArray, Person::compareByAge);

举个更简单例子:

//对一个Integer列表进行排序,因为Integer中已经存在静态的比较方法compare(),因此可以直接用静态方法引用的方式来调用
public class Test {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(82,22,34,50,9);
        list.sort(Integer::compare);
        System.out.println(list);
    }
}

//结果:[9, 22, 34, 50, 82]

3.2.2 实例方法引用

实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用类无需实例化,直接用类名去调用。
@Data
class User {

    private String name;
    private Integer age;

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

public class TestInstanceReference {

    public static void main(String[] args) {

        TestInstanceReference test = new TestInstanceReference();
        User user = new User("欧阳峰",32);
        Supplier<String> supplier = () -> user.getName();
        System.out.println("Lambda表达式输出结果:" + supplier.get());

        Supplier<String> supplier2 = user::getName;
        System.out.println("实例方法引用输出结果:" + supplier2.get());
    }
}

//输出结果:
//Lambda表达式输出结果:欧阳峰
//实例方法引用输出结果:欧阳峰

3.2.3 对象方法引用

若Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数时,可以使用对象方法引用。
//String的equals()方法:
public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
}


/**
 * BiPredicate的test()方法接受两个参数,x和y,具体实现为x.equals(y),
 * 满足Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数,因此可以使用对象方法引用。
 */
public static void main(String[] args) {

   BiPredicate<String,String> bp = (x, y) -> x.equals(y);
   BiPredicate<String,String> bp1 = String::equals;

   boolean test = bp1.test("xy", "xx");
   System.out.println(test);
}

3.2.4 构造方法引用

注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致。
//要获取一个空的User列表
Supplier<List<User>> userSupplier = () -> new ArrayList<>();
List<User> user = userSupplier.get();

Supplier<List<User>> userSupplier2 = ArrayList<User>::new;    // 构造方法引用写法
List<User> user2 = userSupplier.get();

3.3 StreamAPI

步骤1、创建Stream - 从一个数据源,如集合、数组中获取流。

步骤2、中间操作 - 一个操作的中间链,对数据源的数据进行操作。

步骤3、终止操作 - 一个终止操作,执行中间操作链,并产生结果。

3.3.1 获取stream对象

1. 从集合或者数组中获取
   Collection.stream(), //串行 如:list.stream()
   Collection.parallelStream()  //并行
   Arrays.stream(T t)    
       
2. BufferReader
   BufferReader.lines() -> stream()
       
3. 静态工厂
   java.util.stream.IntStream#range()..
   java.nio.file.Files#walk()....

4. 自定义构建
   java.util.Spliterator    

3.3.2 中间操作API{ intermediate}

操作结果是一个stream,中间操作可以有一个或者多个连续的中间操作,需要注意的是,
中间操作只记录操作方式,不做具体执行,直到结束操作发生时,才做数据的最终执行。

*中间操作过程*:
无状态:数据处理时,不受前置中间操作的影响,主要包括[map/filter/peek/parallel/sequential/unordered]
有状态:数据处理时,受到前置中间操作的影响,主要包括[distinct/sorted/limit/skip]

3.3.3 终结操作|结束操作{Terminal}

需要注意:
    
1.一个Stream对象,只能有一个Terminal操作,这个操作一旦发生,就会真实处理数据,生成对应的数据;
    
2.终结操作:
  非短路操作:当前的stream对象必须处理完集合中所有数据,才能得到处理结果;主要包括:
            forEach/forEachOrdered/toArray/reduce/collect/min/max/count/iterator
    
  短路操作:当前的stream对象在处理过程中,一旦满足某个条件,就可以得到结果
          anyMatch/allMatch/findFirst/findAny等  

3.4 Stream操作集合中的数据

3.4.1 其它类型->stream对象

//多个数据
Stream stream = Stream.of("admin", "Jack", "Tom");

//数组
String[] strArr = new String[]{"admin", "Jack"};
Stream stream1 = Arrays.stream(strArr);

//列表
List<String> lists = Lists.newArrayList();
Stream<String> stream2 = lists.stream();

//集合
Set<String> set = Sets.newHashSet();
Stream<String> stream3 = set.stream();

//Map
Map<String, Integer> map = Maps.newHashMap();
Stream<Map.Entry<String, Integer>> stream4 = map.entrySet().stream();

在数据运算中,会对基本数据类型进行频繁的装箱拆箱操作,所以对于基本类型stream进行了一些基本的封装:

IntStream.of(new int[]{10,20,30}).forEach(System.out::println);
IntStream.range(1, 5).forEach(System.out::println);

3.4.2 stream对象->其它类型

Stream streams = Stream.of("admin", "Jack", "Tom");
//数组
Object[] objx = streams.toArray(String[]::new);

//字符串
String str = streams.collect(Collectors.joining()).toString();

//列表
List<String> listx = (List<String>) stream.collect(Collectors.toList());

//集合
Set<String> setx = (Set<String>) streams.collect(Collectors.toSet());

//Map
Map<String, String> mapx = (Map<String, String>) streams.collect(Collectors.toMap(x -> x, y -> "value:" + y));

3.4.3 stream常见API操作

1. map()方法


    /**
     * map()方法的源码
     * Returns a stream consisting of the results of applying the given function to the elements of this stream.
     * @param <R> The element type of the new stream
     * @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
     *               <a href="package-summary.html#Statelessness">stateless</a>
     *               function to apply to each element
     * @return the new stream
     */
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);

map()方法的参数为Function(函数式接口)对象,map()方法将流中的所有元素用Function对象进行运算,生成新的流对象(流的元素类型可能改变)。举例如下:

    public static void main(String[] args) {  
        List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);
        //用map()方法计算了所有数组元素的绝对值并生成了一个新的流,然后再用forEach()遍历打印。
        numbers.stream().map( n -> Math.abs(n)).forEach(n ->  System.out.println("Element abs: " + n));
    }

2. flatMap()方法

跟map()方法不同的是,Function函数的返回值类型是Stream<? extends R>类型,而不是R类型,即Function函数返回一个Stream流,
这样flatMap()能够将一个二维的集合映射成一个一维的集合,比map()方法拥有更高的映射深度
//源码
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
//有一个字符串数组:其有三个元素,每个元素有两个数组并用空格隔开,如果每个元素以空格分割成2个元素,并遍历打印这6个元素
List<String> list = Arrays.asList("1 2", "3 4", "5 6");
//用flatMap()方法如下:
list.stream().flatMap(item -> Arrays.stream(item.split(" "))).forEach(System.out::println);


//用map()方法返回了一个“流中流”,需要在每个Stream元素遍历时,再加一层forEach进行遍历
 list.stream().map(item -> Arrays.stream(item.split(" "))).forEach(n ->n.forEach(System.out::println));

3.filter()方法

    /**
     * 源码
     * Returns a stream consisting of the elements of this stream that match the given predicate.
     *
     * <p>This is an <a href="package-summary.html#StreamOps">intermediate operation</a>.
     *
     * @param predicate a <a href="package-summary.html#NonInterference">non-interfering</a>,
     *                  <a href="package-summary.html#Statelessness">stateless</a>
     *                  predicate to apply to each element to determine if it  should be included
     * @return the new stream
     */
    Stream<T> filter(Predicate<? super T> predicate);

filter()方法的参数为Predicate(函数式接口)对象,再lambda表达式的讲解中我们提到过这个接口,一般用它进行过滤

    public static void main(String[] args) {  
        List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);
      
        long count = numbers.parallelStream().filter(i -> i>0).count();
        
        System.out.println("Positive count: " + count);
    }

4.reduce()方法

//reduce操作又称为折叠操作,用于将流中的所有值合成一个。reduce()方法的源码
Optional<T> reduce(BinaryOperator<T> accumulator);
reduce()方法参数为BinaryOperator类型的累加器(它接受两个类型相同的参数,返回值类型跟参数类型相同),返回一个Optional对象。
实际上,Stream API中的mapToInt()方法返回的IntStream接口有类似的 average()、count()、sum()等方法就是做reduce操作,
类似的还有mapToLong()、mapToDouble() 方法。当然,我们也可以用reduce()方法来自定义reduce操作。

 
//例如我们用reduce()方法来进行整数数组求和操作    
public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(-1, -2, 0, -1, 4, 5, 1);
    
    Integer total = numbers.stream().reduce((t, n) -> t + n).get();
    
    System.out.println("Total: " + total);
}    

5.collect()方法

    /**
     * @param <R> the type of the result
     * @param <A> the intermediate accumulation type of the {@code Collector}
     * @param collector the {@code Collector} describing the reduction
     * @return the result of the reduction
     */
    <R, A> R collect(Collector<? super T, A, R> collector);
collect()方法的参数为一个java.util.stream.Collector类型对象,可以用java.util.stream.Collectors工具类提供的静态方法来生成,
Collectors类实现很多的归约操作,如Collectors.toList()、Collectors.toSet()、Collectors.joining()(joining适用于字符串流)等
    

//用map()方法生成新的流,再用collect()方法返回原数组的绝对值数组
public static void main(String[] args) {  
    List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);
    
    List<Integer> abss = numbers.stream().map( n -> Math.abs(n)).collect(Collectors.toList());
    
    System.out.println("Abs list: " + abss);
}    

第4章 Optional类

4.1 背景

先来看一下不使用Optional类时,我们为了防止NullPointerException会怎么处理

@Data
public class Person {

    private String username;

    public Person getParent() {
        return new Person();
    }

    //原始逻辑判断空代码
    public String getParentName(Person son) {
        if (son != null) {
            Person parent = son.getParent();
            if (parent != null) {
                return parent.getUsername();
            } else {
                return "---";
            }
        }
        return "---";
    }

    //Optional代码
    public String getParentNameOptional(Person son) {
        return Optional.ofNullable(son).map(Person::getParent).map(Person::getUsername).orElse("---");
    }

}

4.2 Optional类简介

java.util.Optional类的引入很好的解决空指针异常,类声明如下:
    public final class Optional {}

java.util.Optional类是一个封装了Optional值的容器对象,Optional值可以为null,如果值存在,调用isPresent()方法返回true,调用get()方法可以获取值。

通过源代码会发现,它并没有实现java.io.Serializable接口,因此应避免在类属性中使用,防止意想不到的问题。
    
除了Optional类之外,还扩展了一些常用类型的Optional对象,比如:OptionalDouble、OptionalInt、OptionalLong。    

4.3 创建Optional对象

4.3.1 创建对象

创建Optional对象有三种方法:empty()、of()、ofNullable(),均为静态方法。

1.如果Optional对象没有值则用empty()方法

Optional empty = Optional.empty();

2.如果确定Optional对象的值不为null,则可用of()方法

Optional stringOptional = Optional.of("Hello 公众号:假如我是你");

3.如果不确定Optional对象的值是否为null,则可用ofNullable()

比如上面,不确定Person对象是不否null,就用了ofNullable()方法。当然,也可以直接给该方法传null
    
Optional ofNullOptional = Optional.ofNullable(null);

此时,通过调用其isPresent方法可以查看该Optional中是否值为null。
boolean bool = ofNullOptional.isPresent();
System.out.println(bool);

4.3.2 获取对象的值

1.get获取Optional中的值

此时如果直接调用get方法获取值,则会抛出异常 java.util.NoSuchElementException: No value present
ofNullOptional.get();    

需要另外一个方法的辅助:isPresent()。该方法可判定Optional中是否有值,如果有则返回true,如果没有则返回false。
Optional ofNullable1 = Optional.ofNullable(null);
boolean flag = ofNullable1.isPresent();
if (flag) {
   ofNullable1.get();
}    

2.map获取Optional中的值

//对于对象操作,也可以通过map来获取值
Optional.ofNullable(son).map(Person::getParent)

注:map方法,如果有值,则对其执行调用映射函数得到返回值。如果返回值不为null,
   则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional。   

3.flatMap获取Optional中的值

如果有值,则返回Optional类型返回值,否则返回空Optional。flatMap与map方法类似。
但flatMap中的mapper返回值必须是Optional。调用结束时,flatMap不会对结果用Optional封装。

Optional sonOptional = Optional.ofNullable(son);
sonOptional.flatMap(OptionalTest::getOptionalPerson);    

4.orElse获取Optional中的值

orElse方法,如果有值就返回,否则返回一个给定的值作为默认值;
Optional.empty().orElse("--"); 
等价于 str != null ? str : "--"

5.orElseGet获取Optional中的值

orElseGet()方法与orElse()方法作用类似,但生成默认值的方式不同。该方法接受一个Supplier函数式接口参数,用于生成默认值;

//这里可以处理更多的业务逻辑    
Optional.empty().orElseGet(() -> {
    String a = "1";
    String b = "1";
    return a + b;
});    

6.orElseThrow获取Optional中的值

orElseThrow()方法与get()方法类似,当值为null时调用会抛出NullPointerException异常,但该方法可以指定抛出的异常类型。

Optional.empty().orElseThrow(()-> new RuntimeException("请先关注公众号!"));    

7.filter()方法过滤

filter()方法可用于判断Optional对象是否满足给定条件,一般用于条件过滤:

Optional<String> who = Optional.of("我是谁, 不一样的存在").filter((val) -> {
    return val.contains("我是谁");
});

System.out.println(who.get());    

示例:

     public static void main(String[] args) {
        UserService userService = new UserService();
        User user = userService.getUserByName("张三");
        /**
         * jdk8以前的做法
         */
        if (user != null) {
            System.out.println("user = " + user);
        } else {
            System.out.println("为空的业务逻辑");
        }
        /**
         * 使用Optional
         * 与if ...else差别不大
         */
        Optional<User> optional = Optional.ofNullable(user);
        if (optional.isPresent()) {
            System.out.println("user不为null");
        } else {
            System.out.println("user为Null的业务逻辑");
        }

        /**
         * 如果optional中的user不为空,则会执行,否则不执行
         * 某些情况下,这样的逻辑刚好可用
         */
        optional.ifPresent((t -> {
            System.out.println("t = " + t);
        }));

        /**
         * 使用map方法,进行业务逻辑处理,
         */
        optional.map(t -> {
            // 业务逻辑处理
            return "user不为空";
        }).orElse(null);
    }

版权声明:本文中所有内容均属于阿里云开发者社区所有,任何媒体、网站或个人未经阿里云开发者社区协议授权不得转载、链接、转贴或以其他方式复制发布/发表。申请授权请邮件developerteam@list.alibaba-inc.com,已获得阿里云开发者社区协议授权的媒体、网站,在转载使用时必须注明"稿件来源:阿里云开发者社区,原文作者姓名",违者本社区将依法追究责任。 如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:developer2020@service.aliyun.com 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章