開發與維運

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);
    }

Leave a Reply

Your email address will not be published. Required fields are marked *