其他分享
首页 > 其他分享> > optional类,便利函数,创建Optional,Optional对象操作以及Optional流

optional类,便利函数,创建Optional,Optional对象操作以及Optional流

作者:互联网

java.util.optional

前言

我们必须考虑在一个空流中获取元素会发生什么,我们总是习惯流是不会中断的。然而,在流中放置null却会轻易令其中断,那么是否可以有一种对象,可以使我们即在持有流元素的同时,也可以在元素不存在时也向我们进行友好提示呢(不会产生异常)

一、 Optional 类

Optional 可以实现这样的功能。标准流操作中的一些可以返回 Optional 对象,因为它们并不能保证预期结果一定存在。

reduce()不再以identity形式开头,而是将其返回值包含在 Optional 中。(identity 对象成为其他形式的reduce()的默认结果,因此不存在空结果的风险)对于数字流 IntStream 等,average()会将结果包装在 Optional 以防止流为空。

public class OptionalsFromEmptyStreams {
    public static void main(String[] args) {
        System.out.println(Stream.<String>empty()
                .findFirst());
        System.out.println(Stream.<String>empty()
                .findAny());
        System.out.println(Stream.<String>empty()
                .max(String.CASE_INSENSITIVE_ORDER));
        System.out.println(Stream.<String>empty()
                .min(String.CASE_INSENSITIVE_ORDER));
        System.out.println(Stream.<String>empty()
                .reduce((s1, s2) -> s1 + s2));
        System.out.println(IntStream.empty()
                .average());
    }
}
/*
output:
Optional.empty
Optional.empty
Optional.empty
Optional.empty
Optional.empty
OptionalDouble.empty
 */

当流为空的时候你会获得一个Optional.empty 对象,而不是抛出异常。Optional 拥有toString()方法可以用于展示有用信息。
注意:空流是通过Stream.<String>empty()创建的。如果在没有任何上下文环境的情况下调用Stream.empty(),Java 并不知道它的数据类型。

下面是Optional 的基本用法:

public class OptionalBasics {
    static void test(Optional<String> optString) {
        if (optString.isPresent())
            System.out.println(optString.get());
        else
            System.out.println("Nothing inside!");
    }

    public static void main(String[] args) {
        test(Stream.of("Epithets").findFirst());
        test(Stream.<String>empty().findFirst());
    }
}
/*
output:
Epithets
Nothing inside!
 */

当你接收到Optional 对象时,应首先调用isPresent()检查其中是否包含元素,如果存在,可使用get()获取。

二、 便利函数

有许多便利函数可以解包 Optional ,

public class Optionals {
    static void basics(Optional<String> optString) {
        if (optString.isPresent()) {
            System.out.println(optString.get());
        } else System.out.println("Nothing Inside!");
    }

    static void ifPresent(Optional<String> optString) {
        optString.ifPresent(System.out::println);
    }

    static void orElse(Optional<String> optString) {
        System.out.println(optString.orElse("Nada"));
    }

    static void orElseGet(Optional<String> optString) {
        System.out.println(optString.orElseGet(() -> "Generated"));
    }

    static void orElseThrow(Optional<String> optString) {
        try {
            System.out.println(optString.orElseThrow(() -> new Exception("Supplied")));
        } catch (Exception e) {
            System.out.println("Caught " + e);
        }
    }

    static void test(String testName, Consumer<Optional<String>> cos) {
        System.out.println("===" + testName + "=====");
        cos.accept(Stream.of("Epithets").findFirst());
        cos.accept(Stream.<String>empty().findFirst());
    }

    public static void main(String[] args) {
        test("basics", Optionals::basics);
        test("ifPresent", Optionals::ifPresent);
        test("orElse", Optionals::orElse);
        test("orElseGet", Optionals::orElseGet);
        test("orElseThrow", Optionals::orElseThrow);
    }
}
/*
output:
===basics=====
Epithets
Nothing Inside!
===ifPresent=====
Epithets
===orElse=====
Epithets
Nada
===orElseGet=====
Epithets
Generated
===orElseThrow=====
Epithets
Caught java.lang.Exception: Supplied
 */

三、 创建 Optional

当我们在自己的代码块中加入 Optional 时,可以使用以下三个静态方法:

下面是示例:

public class CreatingOptionals {
    static void test(String testName, Optional<String> optional) {
        System.out.println("====" + testName + "====");
        System.out.println(optional.orElse("NULL"));
    }

    public static void main(String[] args) {
        test("empty", Optional.empty());
        test("of", Optional.of("Howdy()"));
        try {
            test("of", Optional.of(null));
        } catch (Exception e) {
            System.out.println(e);
        }

        test("ofNullable", Optional.ofNullable("Hi"));
        test("ofNullable", Optional.ofNullable(null));
    }
}
/*
output:
====empty====
NULL
====of====
Howdy()
java.lang.NullPointerException
====ofNullable====
Hi
====ofNullable====
NULL
 */

我们不能通过传递 null 到 of() 来创建 Optional 对象,最安全的方法是:使用 ofNullable() 来优雅的处理 null。

四、 Optional 对象操作:filter、map、flatMap

当我们的流管道生产了 Optional 对象,下面三个方法可使得 Optional 的后续能做更多的操作:

public class OptionalFilter {
    static String[] elements = {"Foo", "Bar", "Baz", "Bingo"};
    static Stream<String> testStream() {
        return Arrays.stream(elements);
    }

    static void test(String descr, Predicate<String> predicate) {
        System.out.println("---(" + descr + ")----");
        for (int i = 0; i <= elements.length; i++) {
            System.out.println(testStream()
                    .skip(i)
                    .findFirst()
                    .filter(predicate));
        }
    }

    public static void main(String[] args) {
        test("true", s -> true);
        test("false", s -> false);
        test("s != \"\"", s -> s != "");
        //此处的s.length() 是传入的单个流元素
        test("s.length() == 3", s -> s.length() == 3);
        test("startsWith(\"B\")", s -> s.startsWith("B"));
    }
}
/*
output:
---(true)----
Optional[Foo]
Optional[Bar]
Optional[Baz]
Optional[Bingo]
Optional.empty
---(false)----
Optional.empty
Optional.empty
Optional.empty
Optional.empty
Optional.empty
---(s != "")----
Optional[Foo]
Optional[Bar]
Optional[Baz]
Optional[Bingo]
Optional.empty
---(s.length() == 3)----
Optional[Foo]
Optional[Bar]
Optional[Baz]
Optional.empty
Optional.empty
---(startsWith("B"))----
Optional.empty
Optional[Bar]
Optional[Baz]
Optional[Bingo]
Optional.empty
 */

即使输出看起来像流,要特别注意 test() 中的for循环:1.每次for循环都操作的是新生成的流,2.跳过指定数量的元素,3.通过调用findFirst()获取剩余元素中的第一个元素,并将其包装在 Optional 对象中。
注意:这里不同于普通for 循环,这里的 等于 号实际上使得最后一位超出了数组转化为的流的范围,但是并不会报错

map()一样,Optional.map()仅仅在 Optional 不为空时才执行这个映射函数,并将 Optional 的内容提取出来,传递给映射函数

public class OptionalMap {
    static String[] elements = {"12", "", "23", "45"};
    static Stream<String> stream() {
        return Arrays.stream(elements);
    }

    static void test(String descr, Function<String,String> func) {
        System.out.println("---(" + descr + ")----");
        for (int i = 0; i <= elements.length; i++) {
            System.out.println(stream()
                    .skip(i)
                    .findFirst()//此处产生一个optional
                    .map(func));
        }
    }

    public static void main(String[] args) {
        test("Add brackets", s -> "[" + s + "]");
        test("Increment", s -> {
            try {
                return Integer.parseInt(s) + 1 + "";
            } catch (Exception e) {
                return s;
            }
        });
        test("Replace", s -> s.replace("2", "9"));
        //记得每个s是单个流元素
        test("Take last digit", s -> s.length() > 0 ? s.charAt(s.length() - 1) + "" : s);
    }
}
/*
output:
---(Add brackets)----
Optional[[12]]
Optional[[]]
Optional[[23]]
Optional[[45]]
Optional.empty
---(Increment)----
Optional[13]
Optional[]
Optional[24]
Optional[46]
Optional.empty
---(Replace)----
Optional[19]
Optional[]
Optional[93]
Optional[45]
Optional.empty
---(Take last digit)----
Optional[2]
Optional[]
Optional[3]
Optional[5]
Optional.empty
 */

映射函数的返回结果会自动包装成为 Optional。Optional.empty() (空)会被直接跳过。

public class OptionalFlatMap {
    static String[] elements = {"12","", "23", "45"};

    static Stream<String> stream() {
        return Arrays.stream(elements);
    }

    static void test(String descr, Function<String, Optional<String>> func) {
        System.out.println("---(" + descr + ")----");
        for (int i = 0; i <= elements.length; i++) {
            System.out.println(stream()
                    .skip(i)
                    .findFirst()
                    .flatMap(func));//这里的参数变成了Optional类型
        }
    }

    public static void main(String[] args) {
        test("Add brackets", new Function<String, Optional<String>>() {
            @Override
            public Optional<String> apply(String s) {
                return Optional.of("[" + s + "]");
            }
        });
        test("Increment",s -> {
            try {
                return Optional.of(Integer.parseInt(s) + 1 + "");
            } catch (Exception e) {
                return Optional.of(s);
            }
        });
        test("Replace", s -> Optional.ofNullable(s.replace("2", "9")));
        test("Take last digit",s->Optional.of(s.length()>0?s.charAt(s.length()-1)+"":s));
    }
}
/*
output:
---(Add brackets)----
Optional[[12]]
Optional[[]]
Optional[[23]]
Optional[[45]]
Optional.empty
---(Increment)----
Optional[13]
Optional[]
Optional[24]
Optional[46]
Optional.empty
---(Replace)----
Optional[19]
Optional[]
Optional[93]
Optional[45]
Optional.empty
---(Take last digit)----
Optional[2]
Optional[]
Optional[3]
Optional[5]
Optional.empty
 */

Optional 的 flatMap()应用于已生成 Optional 的映射函数,所以它不会像 map()那样将结果封装在 Optional 里。
map()flatMap()将提取非空的Optional 内容将其应用在映射函数。唯一的区别就是它不再将结果包装在 Optional 中,因为已经包装过了。

五、 Optional 流

假设你的生成器可能产生 null 值。那么当用它来创建流时,用 Optional 来包装元素更好。

public class Signal {
    private final String msg;

    public Signal(String msg) {
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }

    @Override
    public String toString() {
        return "Signal{" +
                "msg='" + msg + '\'' +
                '}';
    }

    static Random rand = new Random(47);
    public static Signal morse() {
        switch (rand.nextInt(4)) {
            case 1:
                return new Signal("dot");
            case 2:
                return new Signal("dash");
            default:
                return null;
        }
    }

    public static Stream<Optional<Signal>> stream() {
        return Stream.generate(Signal::morse)
                .map(signal -> Optional.ofNullable(signal));
    }
}

当我们使用这个流的时候,必须要弄清楚如何解包 Optional 。代码如下:

public class StreamOfOptionals {
    public static void main(String[] args) {
        Signal.stream()
                .limit(10)
                .forEach(System.out::println);
        System.out.println("-----------");
        Signal.stream()
                .limit(10)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .forEach(System.out::println);
    }
}
/*
output:
Optional[Signal{msg='dash'}]
Optional[Signal{msg='dot'}]
Optional[Signal{msg='dash'}]
Optional.empty
Optional.empty
Optional[Signal{msg='dash'}]
Optional.empty
Optional[Signal{msg='dot'}]
Optional[Signal{msg='dash'}]
Optional[Signal{msg='dash'}]
-----------
Signal{msg='dot'}
Signal{msg='dot'}
Signal{msg='dash'}
Signal{msg='dash'}
 */

在这里我们使用filter()来保留那些非空 Optional, 然后在 map() 中使用get()获取元素。由于每种情况都需要定义“空值”的含义,所以通常我们要为每个应用程序采用不同的方法。

标签:Optional,System,println,static,便利,test,optional,empty
来源: https://blog.csdn.net/weixin_43621315/article/details/118003975