java代码之美(16) ---Java8 Optional

摘自:https://www.cnblogs.com/qdhxhz/p/12056745.html

Java8 Optional

一句话介绍Optional类:使用JDK8的Optional类来防止NullPointerException(空指针异常)问题

一、前言

在我们开放过程中,碰到的异常中NullPointerException必然是排行第一的。所以在平时编码中,我们会时时的判断null

public void saveCity(City city) {
        if (city != null) {
            String cityName = city.getCityName();
            if (cityName != null) {
                String code = cityDao.findCodeByName(cityName);
                city.setCode(code);
                cityDao.save(city);
            }
        }
    }

虽然上面代码变得更加安全,但是过多嵌套 if 语句降低代码整体可读性,提高复杂度。我们可以优化下代码

    public void saveCity(City city) {
        if (city == null) {
            return;
        }
        String cityName = city.getCityName();
        if (cityName == null) {
            return;
        }
        String code = cityDao.findCodeByName(cityName);
        city.setCode(code);
        cityDao.save(city);
    }

这样还可以,但我们通过Optional变的更简洁

    public void saveCity(City city) {
        //就一行 city不为空返回 城市名称 否则直接返回空
        Optional<String> roleOpt = Optional.ofNullable(city).map(City::getCityName);
        //如果容器中 不为空
        if (roleOpt.isPresent()) {
            String code = cityDao.findCodeByName(roleOpt.get());
            city.setCode(code);
            cityDao.save(city);
        }
    }

这样,我们仅需要对我们关心的做一次校验,省却了前面的一系列的检验操作。

二、Optional API

概念 Optiona本质是一个容器,容器中存在为null或者不包含非null值的容器对象。提供了一系列的方法供我们判断该容器里的对象是否存在。

1、JDK源码

/**
 * final修饰代表不能被子类继承
 */
public final class Optional<T> {
    /**
     * 创建一个空容器
     */
    private static final java.util.Optional<?> EMPTY = new java.util.Optional<>();

    /**
     * 传入的值
     */
    private final T value;

    /**
     * 构造函数私有化 说明不能被外部new
     */
    private Optional() {
        this.value = null;
    }

    /**
     * 私有化构造函数
     */
    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

    /**
     * 获取空容器
     */
    public static <T> java.util.Optional<T> empty() {
        @SuppressWarnings("unchecked")
        java.util.Optional<T> t = (java.util.Optional<T>) EMPTY;
        return t;
    }

    /**
     * 传入的对象不能为空 否则抛异常
     */
    public static <T> java.util.Optional<T> of(T value) {
        return new java.util.Optional<>(value);
    }

    /**
     * 传入的对象可以为空
     */
    public static <T> java.util.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 T orElse(T other) {
        return value != null ? value : other;
    }

    //==========有关下面这几个JDK8自带的函数式接口的作用,上一篇博客有详细说明,这里就不多说了。

    /**
     * 传入Consumer编程式接口参数
     */
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

    /**
     * 传入Predicate编程式接口参数
     */
    public java.util.Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

    /**
     * 传入Function编程式接口参数
     */
    public <U> java.util.Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return java.util.Optional.ofNullable(mapper.apply(value));
        }
    }

    /**
     * 传入Function编程式接口参数
     */
    public <U> java.util.Optional<U> flatMap(Function<? super T, java.util.Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

    /**
     * 传入Supplier编程式接口参数
     */
    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }

    /**
     * 传入Supplier编程式接口参数
     */
    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }
}

2、创建Optional对象

通过上面源码可以看出,Optional的构造函数都是私有化的,无法直接new对象。它这边提供了3个静态方法获取对象。

1、创建一个一定是空的Optional容器

Optional<Car> optCar = Optional.empty();

2、创建一个一定是非空值Optional容器(传入的对象不可以为null,否则抛出NullPointerException)

Optional<Car> optUser = Optional.of(user);

3、创建一个可能是空也可能不为空的Optional容器(传入的对象可以为null)

Optional<Car> optUser = Optional.ofNullable(user);

3、总结常用方法

1、isPresent()        //有值则返回true
2、get():             //值存在时返回值,否则抛出一个NoSuchElement异常(所以调这个,一般先判断上面方法返回是否为true)
3、orElse(T other)    //值存在时返回值,否则返回一个默认值
4、ifPresent(Consumer<T> block)             //会在值存在的时候执行给定的代码块
5、orElseThrow(Supplier<? extends X> exceptionSupplier)  //与get()类似,不同的是可以自定义异常类型
6、orElseGet(Supplier<? extends T> other)   //orElse方法的延迟调用版,Supplier方法只有在Optional对象不含值时才执行调用
7、map/flatMap/filter                       //与Stream中用法类似

三、完整的示例

这里写一个针对以上API都涉及到的Demo,这个例子明白了,那么Optional的使用也就都清楚了。

代码

public class OptionalDemo {
    public static void main(String[] args) {
        //1、创建Optional实例,传入的对象不能为null
        Optional<String> nameOptional = Optional.of("张三");

        //2、创建Optional实例,传入对象可以为null,也可以不weinull
        Optional emptyOptional = Optional.ofNullable(null);

        //3、isPresent方法用来检查Optional实例是否有值。
        if (nameOptional.isPresent()) {
            //调用get()返回Optional值。
            System.out.println("1、" + nameOptional.get());
        }

        try {
            //4、在Optional实例上调用get()抛出NoSuchElementException。
            System.out.println("2、" + emptyOptional.get());
        } catch (NoSuchElementException ex) {
            System.out.println("3、异常" + ex.getMessage());
        }

        //
        //5、如果Optional值不为空,lambda表达式会处理并在其上执行操作。(这里x代表就是nameOptional中的对象)
        nameOptional.ifPresent((x) -> {
            System.out.println("4、字符串长度为: " + x.length());
        });

        //6、如果有值orElse方法会返回Optional实例,没值则返回当前值
        System.out.println("5、"+ emptyOptional.orElse("如果是空容器则返回李四"));
        System.out.println("6、"+nameOptional.orElse("如果是空容器则返回王五"));

        //7、orElseGet与orElse类似,区别在于传入的参数不同,一个是直接传入对象,这个是传入Supplier函数式接口
        System.out.println("7、" + emptyOptional.orElseGet(() -> "李四"));
        System.out.println("8、" + nameOptional.orElseGet(() -> "王五"));

        try {
            //8、如果是空容器,则可以抛出自定义异常。
            emptyOptional.orElseThrow(() -> new NullPointerException("空容器异常"));
        } catch (Throwable ex) {
            System.out.println("9、" + ex.getMessage());
        }

        Optional<String> ageOptional = Optional.of("10");
        //9、这里入参是Function,所以可以转换容器中的对象 好比将String对象转为Integer对象
        Optional<Integer> age = ageOptional.map((value) -> Integer.parseInt(value));
        /**
         * 10、flatMap与map(Funtion)非常相似,不同在于 map返回可以将String对象转为Integer对象,但flatMap转换后一定还是String对象
         */
        Optional<String> upperName = nameOptional.flatMap((value) -> Optional.of(value.toUpperCase()));

        //11、filter方法检查Optiona值是否满足给定条件。如果满足返回Optional实例值,否则返回空Optional。
        Optional<String> longName = nameOptional.filter((value) -> value.length() > 6);
        System.out.println("10、" + longName.orElse("longName容器的名字长度小于6位"));

        //12、另一个示例,Optional满足给定条件。
        Optional<String> anotherName = Optional.of("乌啦啦市长公主");
        Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
        System.out.println("11、" + shortName.orElse("anotherName容器的名字长度小于6位"));

    }
}

运行结果

参考

1、JDK8新特性之:Optional

2、Optional类包含的方法介绍及其示例

你如果愿意有所作为,就必须有始有终。(26)

分类: 【Java】-- 代码之美

原文地址:https://www.cnblogs.com/xichji/p/12340171.html

时间: 2024-08-28 19:15:37

java代码之美(16) ---Java8 Optional的相关文章

【java代码之美】---Java8 Stream

Stream 第一次看到Stream表达式就深深把我吸引,用它可以使你的代码更加整洁而且对集合的操作效率也会大大提高,如果你还没有用到java8的Stream特性,那就说明你确实out啦. 一.概述 1.什么是Stream Stream是一种可供流式操作的数据视图有些类似数据库中视图的概念它不改变源数据集合如果对其进行改变的操作它会返回一个新的数据集合. 总的来讲它有三大特性:在之后我们会对照着详细说明        1.stream不存储数据        2.stream不改变源数据    

【java代码之美】---Java8 Map中的computeIfAbsent方法

Map中的computeIfAbsent方法 Map接口的实现类如HashMap,ConcurrentHashMap,HashTable等继承了此方法,通过此方法可以在特定需求下,让你的代码更加简洁. 一.案例说明 1.概述 在JAVA8的Map接口中,增加了一个方法computeIfAbsent,此方法签名如下: public V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) 此方法首先判断缓存

java代码之美(14)---Java8 函数式接口

Java8 函数式接口 之前写了有关JDK8的Lambda表达式:java代码之美(1)---Java8 Lambda 函数式接口可以理解就是为Lambda服务的,它们组合在一起可以让你的代码看去更加简洁. 一.概念 1.什么是函数式接口 概念 所谓的函数式接口, 当然首先是一个接口, 然后就是在这个接口里面 只能有一个抽象方法. 有关函数式接口,有个专门的注解叫:@FunctionalInterface.该注解主要特点有: 1.该注解只能标记在"有且仅有一个抽象方法"的接口上,表示函

【java代码之美】---guava之Immutable(不可变)集合

Immutable(不可变)集合 一.概述 guava是google的一个库,弥补了java语言的很多方面的不足,很多在java8中已有实现,暂时不展开.Collections是jdk提供的一个工具类. Guava中不可变对象和Collections工具类的unmodifiableSet/List/Map/etc的区别: 当Collections创建的不可变集合的wrapper类改变的时候,不可变集合也会改变,而Guava的Immutable集合保证确实是不可变的. 1.JDK中实现immuta

java代码块的理解

最近在复习java基础,在看到java代码块的时候,忽然发现自己貌似对于java代码块一无所知,于是赶紧对着一些资料实战演练了一把. 对于java代码块,不难根据名称看出其实就是一些java语句的集合,以{}的形式出现,共有4中形式: 1.类的方法体 这是我们最常见的一种java代码块,形式如下: 1 public class Boke { 2 public void say(){ 3 System.out.println("我就是代码块的内容啦"); 4 } 5 } say的方法体{

如何写出更好的Java代码

Java是最流行的编程语言之一,但似乎并没有人喜欢使用它.好吧,实际上Java是一门还不错的编程语言,由于最近Java 8发布了,我决定来编辑一个如何能更好地使用Java的列表,这里面包括一些库,实践技巧以及工具. 这篇文章在GitHub上也有.你可以随时在上面贡献或者添加你自己的Java使用技巧或者最佳实践. 编码风格 结构体 builder模式 依赖注入 避免null值 不可变 避免过多的工具类 格式 文档 Stream 部署 框架 Maven 依赖收敛 持续集成 Maven仓储 配置管理

常用的Java代码汇总

1. 字符串有整型的相互转换 Java 1 2 <strong>Stringa=String.valueOf(2);   //integer to numeric string inti=Integer.parseInt(a);//numeric string to an int </strong> 2. 向文件末尾添加内容 Java 1 2 3 4 5 6 7 8 9 10 11 <strong>BufferedWriter out=null; try{ out=ne

eclipse 4.5.2 源码修改 格式化Java代码

注:本文代码基于eclipse4.5.2 1. 需求:在换电脑之后,如何不用配置eclipse就可以很快进入开发呢,并保持原来的编码规范. 2. 方法:修改eclipse源码 分别修改了两个jar包2个类,源码都是来源于eclipse的plugins下对应jar包,具体如下: Jar包 类名 org.eclipse.core.resources_3.10.1.v20150725-1910.jar org.eclipse.core.internal.resources.PreferenceInit

【转】20个常用的经典JAVA代码片段

1. 字符串有整型的相互转换 String a = String.valueOf(2);   //integer to numeric stringint i = Integer.parseInt(a); //numeric string to an int 2. 向文件末尾添加内容 BufferedWriter out = null;try {out = new BufferedWriter(new FileWriter(”filename”, true));out.write(”aStrin