Java篇 - Java8中Optional和StringJoiner的使用与实现

Java9都发布了,现在讲java8有点落伍了,不过我觉得java8相对来说是Java的一个大的分水岭。

Java8有许多好用的新特性,如Lambda,接口方法默认实现,Stream等,但这些都不是本章的重点。本章的重点是两个工具类:Optional和StringJoiner。

 

1. Optional

 

  • 1.1 简介
  1. Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
  2. Optional是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
  3. Optional 类的引入很好的解决空指针异常。

 

  • 1.2 API

static <T> Optional<T> empty()    

  • 返回空的 Optional 实例

boolean equals(Object obj)      

  • 判断其他对象是否等于 Optional

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

  • 如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional

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

  • 如果值存在,返回基于Optional包含的映射方法的值,否则返回一个空的Optional

T get()    

  • 如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException

int hashCode()    

  • 返回存在值的哈希码,如果值不存在返回 0

void ifPresent(Consumer<? super T> consumer)    

  • 如果值存在则使用该值调用consumer,否则不做任何事情

boolean isPresent()    

  • 如果值存在则方法会返回true,否则返回 false

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

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

static <T> Optional<T> of(T value)    

  • 返回一个指定非null值的Optional

static <T> Optional<T> ofNullable(T value)    

  • 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional

T orElse(T other)    

  • 如果存在该值,返回值, 否则返回 other

T orElseGet(Supplier<? extends T> other)  

  • 如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果

<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)    

  • 如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常

String toString()    

  • 返回一个Optional的非空字符串,用来调试 

 


1.3 使用

    public static void main(String[] args) {
        // Optional.ofNullable - 允许传递为 null 参数
        Optional<Integer> num1 = Optional.ofNullable(null);
        // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
        Optional<Integer> num2 = Optional.of(new Integer(10));
        System.out.println(sum(num1, num2));
    }

    private static int sum(Optional<Integer> num1, Optional<Integer> num2) {
        System.out.println("num1值存在: " + num1);
        System.out.println("num2值存在: " + num2);

        // Optional.orElse - 如果值存在,返回它,否则返回默认值
        Integer integer1 = num1.orElse(0);
        // Optional.get - 获取值,值需要存在
        Integer integer2 = num2.get();
        return integer1 + integer2;
    }

执行输出:

num1值存在: Optional.empty
num2值存在: Optional[10]
10

 

  • 1.4 实现原理

先来看看变量与静态工厂的定义:


    // 默认的空Optional对象
    private static final Optional<?> EMPTY = new Optional<>();

    // 实际存储的值
    private final T value;

    // 私有构造器,获取Optional对象只能通过静态工厂方法
    private Optional() {
        this.value = null;
    }
    
    // 静态工厂方法,获取空Optional对象
    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

    // 私有构造器
    private Optional(T value) {
        // value不能为空,为空会抛出异常
        this.value = Objects.requireNonNull(value);
    }
    
    // 静态工厂方法,通过value获取Optional对象,value不能为空
    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }
    
    // 静态工厂方法,通过value获取Optional对象,允许value为空
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

 

再来看看Optional的成员方法:

(1) public T get()

    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
  • 如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException。

(2) public boolean isPresent()

    public boolean isPresent() {
        return value != null;
    }
  • 如果值存在则方法会返回true,否则返回 false。

(3) public boolean isPresent()

    public boolean isPresent() {
        return value != null;
    }
  • 如果值存在则方法会返回true,否则返回 false。

(4) public T orElse(T other)

    public T orElse(T other) {
        return value != null ? value : other;
    }
  • 如果存在该值,返回值, 否则返回 other

其他的我就不再分析了,Optional的实现原理非常简单,同时也充分利用了Java8的新特性,如:Function,Predicate,Consumer,使用起来也非常简单,可以很好的解决程序员空指针的头疼问题。

 

 

2. StringJoiner

 

  • 2.1 简介

StringJoiner用于构造由分隔符分隔的字符序列,并可选择性地从提供的前缀开始和以提供的后缀结尾,省的开发人员再次通过StringBuffer或者StingBuilder拼接。

 

  • 2.2 使用
        StringJoiner sj = new StringJoiner(":", "[", "]");
        sj.add("Kuang").add("Zhong").add("Wen");
        System.out.println(sj.toString());

执行输出: [Kuang:Zhong:Wen]

 

  • 2.3 实现原理

全局变量和构造器:

    private final String prefix;
    private final String delimiter;
    private final String suffix;

    private StringBuilder value;

    private String emptyValue;

    public StringJoiner(CharSequence delimiter) {
        this(delimiter, "", "");
    }

    public StringJoiner(CharSequence delimiter,
                        CharSequence prefix,
                        CharSequence suffix) {
        Objects.requireNonNull(prefix, "The prefix must not be null");
        Objects.requireNonNull(delimiter, "The delimiter must not be null");
        Objects.requireNonNull(suffix, "The suffix must not be null");
        this.prefix = prefix.toString();
        this.delimiter = delimiter.toString();
        this.suffix = suffix.toString();
        this.emptyValue = this.prefix + this.suffix;
    }

定义了prefix(前缀),delimiter(分割符),suffix(后缀),value是存放结果的StringBuilder对象,emptyValue为空结果。

构造器就是传入前缀,分割符和后缀,然后给emptyValue设置默认值(前缀+后缀)。

我觉得emptyValue用String定义不合理,应该用StringBuilder,毕竟StringJoiner除了拼接方便,也要考虑周全String的性能问题。

 

成员方法:

(1) public StringJoiner setEmptyValue(CharSequence emptyValue)

    public StringJoiner setEmptyValue(CharSequence emptyValue) {
        this.emptyValue = Objects.requireNonNull(emptyValue,
            "The empty value must not be null").toString();
        return this;
    }
  • 设置空值,不能为null,为null将抛出异常。

 

(2) public StringJoiner add(CharSequence newElement)

    public StringJoiner add(CharSequence newElement) {
        prepareBuilder().append(newElement);
        return this;
    }

    private StringBuilder prepareBuilder() {
        if (value != null) {
            value.append(delimiter);
        } else {
            value = new StringBuilder().append(prefix);
        }
        return value;
    }
  • 拼接字符,可以看到,在拼接字符前先追加分割符。

 

(3) public StringJoiner merge(StringJoiner other)

    public StringJoiner merge(StringJoiner other) {
        Objects.requireNonNull(other);
        if (other.value != null) {
            StringBuilder builder = prepareBuilder();
            builder.append(other.value, other.prefix.length(), length);
        }
        return this;
    }
  • 合并另一个StringJoiner对象,去掉other的前缀,然后将other的值作为单个字符串追加到当前StringJoiner中,使用的也是StringBuilder。
        StringJoiner sj = new StringJoiner(":", "[", "]");
        sj.add("Kuang").add("Zhong").add("Wen");
        System.out.println(sj.toString());

        StringJoiner sj1 = new StringJoiner(",", "{", "}");
        sj1.add("zhao").add("xue");
        System.out.println(sj1.toString());

        System.out.println("merge = " + sj.merge(sj1).toString());

执行输出:

[Kuang:Zhong:Wen]
{zhao,xue}
merge = [Kuang:Zhong:Wen:zhao,xue]

 

(4) public String toString()

    public String toString() {
        if (value == null) {
            return emptyValue;
        } else {
            if (suffix.equals("")) {
                return value.toString();
            } else {
                int initialLength = value.length();
                String result = value.append(suffix).toString();
                value.setLength(initialLength);
                return result;
            }
        }
    }
  • 如过没有调用过add或者merge方法,value则为null,此时toString将返回空值,否则调用stringBuilder拼接后缀然后返回它的内容。注意,因为stringBuilder.append()可能会对stringBuilder扩容(默认容量16),上面代码中有句setLength,是为了拼接完后缀后,收缩stringBuilder的容量,因为后缀只有在toString时才有用。

 

(5) public int length()

    public int length() {
        return (value != null ? value.length() + suffix.length() :
                emptyValue.length());
    }
  • 获取value的长度,如果value不为空,则返回value的长度 + 前缀的长度,为空的话则返回空value的长度。

 

整体来说,这篇文章比较简单,因为这周轮到单休,团队的事很多,很辛苦,所以我想让自己的大脑休息会。

下一章将介绍Java反射,并且涵盖反射大部分的面试题,周末快乐~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值