Java8用Optional 让“空”中的攻城狮接地气——走进Java Lambda(五)

    Optional是Java8里面用避免空指针的,无论什么语言“空指针异常”总会是最困扰人的东西,老实说这很有可能就是逻辑错误。为了避免这样的错误,Java8建议使用Optional来培养[避免空指针]的好习惯。Optional的方法大纲如下(冒号后表示返回值):

empty() : Optional<T>

of(T) : Optional<T>

ofNullable(T) : Optional<T>

get() : T

isPresent() : boolean

ifPresent(Consumer<? super T>) : void

filter(Predicate<? super T>) : Optional<T>

map(Function<? super T, ? extends U>) : Optional<U>

flatMap(Function<? super T, Optional<U>>) : Optional<U>

orElse(T) : T

orElseGet(Supplier<? extends T>) : T

orElseThrow(Supplier<? extends X>) : T

 

在之前的篇幅中有个求累加的例子是这么写的。

@Test
public void reduceWithInt() {
    int sumAll = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);// 给一个0是用来启动,的,若给-1,结果会是9</span>
    System.out.println(sumAll);// 10
}

例子中调用reduce方法时,我们给了一个初始值0.若我们不给初始值呢会出现什么情况?不给初始值,我们的代码该这样写:

@Test
public void reduceTest() {
    Optional<Integer> sumAll = Stream.of(1, 2, 3, 4).reduce(Integer::sum);// 注意返回值类型
    System.out.println(sumAll);// Optional[10]     注意输出值不再是10了
}

那么这个Optional是个什么呢?Optional<T>是Java用来解决空指针异常的一个东西。不过呢,我们想要得到的结果是10,但返回的是Optional[10],怎么把10弄出来?看下面代码

@Test
public void reduceTest() {
    Integer testInt[]={1, 2, 3, 4};
    Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
    System.out.println(sumAll.get());// 10
}

调用一次Optionalget()方法,就得到真实结果了。猜想一下下面代码会是什么?

@Test
public void reduceTest() {
    Integer testInt[]={};//空数组
    Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
    System.out.println(sumAll.get());
}

居然保错了,亲!

java.util.NoSuchElementException: No value present

at java.util.Optional.get(Optional.java:135)

看到了么,所谓避免“空指针异常”就是用“元素缺失异常”来替换?当然不是啦。这里对空数组没有做任何操作,没有初始值,当然是“null”了,为了不抛异常。只有在确定不为空的时候才调用get()方法了,怎么知道为不为空呢,那就是调用isPresent()方法判断是否“元素存在”,存在就可以调用。比如下面代码:

@Test
public void reduceTest() {
    Integer testInt[]={};
    Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
    System.out.println(sumAll);// Optional.empty
    if(sumAll.isPresent()){
        System.out.println(sumAll.get());
    }else{
        System.out.println(sumAll.empty());// Optional.empty
    }
}

从这段代码,我们看到了,当Optional里面值缺失时,直接打印Optional和调用empty()方法,得到的结果是一样的,即打印“Optional.empty”字符串。你要是觉得多一个判断好麻烦,那么没问题,你可以用ifPresent来代替,在ifPresent里面加入业务逻辑,只有不为空的时候才处理。上面的代码可以改成如下:

@Test
public void reduceTest() {
    Integer testInt[] = {};
    Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
    System.out.println(sumAll);
    sumAll.ifPresent(x -> {
        System.out.println(x); //sumAll不为空的时候,打印x的值;为空的时候,不做任何操作
    });
}

Java8这么搞,究竟几个意思。其实呢,人家的意思就,今后忘掉“空”(null)吧。要养成良好的习惯,不要出现“空”。但是为了兼容“空”状态所以用了一个“缺省”。在上面例子中我们可以给Optional一个默认值0,即如下写法。

@Test
public void reduceTest() {
    Integer testInt[]={};
    Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
    System.out.println(sumAll.orElse(0));// 0
}

这样程序就不会报错了,值缺省(null)时会返回默认值,但是这样写有个问题,那就是默认值是先new出来的,占有一定空间呀,有没有一种方式,缺省时再去new呢。有那就是orElseGet(Supplier<? extends T>),这段代码就可以改成下面这样:

@Test
public void reduceTest() {
    Integer testInt[]={};
    Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
    System.out.println(sumAll.orElseGet(() -> 0)); // 0
}

虽然看到的结果是一样,但是这种写法更省内存空间了。

  可能有这种情况,就是,值出现缺省状态,在业务上就是错了,这个时候你很想抛个特定的异常出来,怎么弄呢。你只需要调用orElseThrow方法就可以了,比如用.orElseThrow(() -> new Throwable(“不能为空”))

   以上所有例子中Optional都是方法返回给我们的,其实我们也可以自己new 一个Optional但是不是使用new关键字。而是使用 of(T)以及ofNullable(T) 等方法。但是ofofNullable是有区别的。我们看看下面这段代码:

@Test
public void reduceTest() {
    Optional<List<Integer>> optional = Optional.of(new ArrayList<>());//of()里面不能传入null
    System.out.println(optional.get()); // []
    Optional<List<Integer>> optional1 = Optional.ofNullable(new ArrayList<>());
    System.out.println(optional1.get()); // []
    Optional<List<Integer>> emptyOptional = Optional.empty();
    Optional<List<Integer>> emptyOptional2 = Optional.ofNullable(null);
    System.out.println(emptyOptional.equals(emptyOptional2)); //true
}

Of里面必须输入一个值,也就是说使用of创建的Optional对象一定不是empty的,而ofNullable是可以创建一个emptyOptional对象。

Optional除了这么苍白地使用,还可配合过滤转换一起使用,比如我们要对{1,2,3,4}累加,若结果大于7我们才返回真实值。那么我们可以这样写:

@Test
public void reduceTest() {
    Integer testInt[]={1,2,3,4};
    Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
    System.out.println(sumAll.filter(x -> x>7));//Optional[10]
}

若计算结果是6,那么打印的值会是Optional.empty。当然还可以进行转换,比如下面是,把小写转换成大写的代码:

@Test
public void reduceTest() {
    String testS[]={"hello"," ","world"," ","!"};
    Optional<String> sumAll = Stream.of(testS).reduce(String::concat);
    System.out.println(sumAll.map(x-> null));//Optional.empty
    System.out.println(sumAll.map(x-> x.toUpperCase()));//Optional[HELLO WORLD !]
}

这里用到了map就得说说OptionalflatMap了,其实flatMapmap的操作非常相似,但是呢,flatMap里面传入的东西必须是Optional对象。我们看到map可以返回null可以返回x.toUpperCase(),但是flatMap是不可以的。上面的例子我们改由flatMap,就该这么写:

@Test
public void reduceTest() {
    String testS[]={"hello"," ","world"," ","!"};
    Optional<String> sumAll = Stream.of(testS).reduce(String::concat);
    System.out.println(sumAll.flatMap(x-> Optional.ofNullable(null)));//Optional.empty
    System.out.println(sumAll.flatMap(x-> Optional.of(x.toUpperCase())));//Optional[HELLO WORLD !]
}

好,至此,Optional里面的方法就算讲完一遍了,没讲的,都是从Object类继承下来的方法,没啥好讲的了。

    原文作者:lumence
    原文地址: https://blog.csdn.net/lsmsrc/article/details/41747241
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞