Java Functional Programming Optional

premise

java.util.OptionalJDK8 is introduced in class, which is famous JDK from Java toolkit Guavaported in. Use the time of this writing is JDK11. OptionalContains a NULLvalue or a non- NULLobject container values, it is often used as clear that no result (in fact clearly indicates that there may also be a result of Optionalrepresentation) method return types, avoid NULLvalue possible anomalies caused by (usually NullPointerException). That is, the method returns a value of type is Optional, you should avoid returning NULL, but should make a point to return value contains the NULLobject Optionalinstance. OptionalThe emergence of NULLjudgment, the filtering operation, a mapping operation provides a functional adaptation entrance, be it an important milestone Java introduced functional programming.

In this paper a new Asciidocpreview mode, you can experience Springthe feeling of the official document:

Optional various methods and use source code analysis scenarios

OptionalThe source code is relatively simple, it is rooted in a simple object container. The following analysis will combine all of its source structure, properties, methods, and use of the corresponding scene.

Optional property and construction

OptionalProperties and constructed as follows:

public final class Optional<T> {

    // 这个是通用的代表NULL值的Optional实例
    private static final Optional<?> EMPTY = new Optional<>();

    // 泛型类型的对象实例
    private final T value;
    
    // 实例化Optional,注意是私有修饰符,value置为NULL
    private Optional() {
        this.value = null;
    }
    
    // 直接返回内部的EMPTY实例
    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
    
    // 通过value实例化Optional,如果value为NULL则抛出NPE
    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }
    
    // 通过value实例化Optional,如果value为NULL则抛出NPE,实际上就是使用Optional(T value)
    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

    // 如果value为NULL则返回EMPTY实例,否则调用Optional#of(value)
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }
    
    // 暂时省略其他代码
}
复制代码

If an object instance is not clear NULL, you should use Optional#of(), for example:

Order o = selectByOrderId(orderId);
assert null != o
Optional op = Optional.of(o);
复制代码

If you can not clear an object instance is NULLwhen you should use Optional#ofNullable(), for example:

Optional op = Optional.ofNullable(selectByOrderId(orderId));
复制代码

Made it clear that holds a NULLvalue of Optionalinstances can use Optional.empty().

get () method

// 如果value为空,则抛出NPE,否则直接返回value
public T get() {
    if (value == null) {
        throw new NoSuchElementException("No value present");
    }
    return value;
}
复制代码

get()The method generally need to be clear valueis not NULLthe time to use it to do a priori valueexistence. E.g:

Order o = selectByOrderId(orderId);
assert null != o
Optional op = Optional.of(o);
Order value = op.get();
复制代码

isPresent () method

// 判断value是否存在,不为NULL则返回true,如果为NULL则返回false
public boolean isPresent() {
    return value != null;
}
复制代码

for example:

Order o = selectByOrderId(orderId);
boolean existed = Optional.ofNullable(o).isPresent();
复制代码

isEmpty () method

isEmpty()JDK11 method is introduced, a isPresent()reverse determination:

// 判断value是否存在,为NULL则返回true,为非NULL则返回false
public boolean isEmpty() {
    return value == null;
}
复制代码

ifPresent () method

ifPresent()Action approach is: If valuenot NULL, use the valueconsumer interface method calls a function of consumers Consumer#accept():

public void ifPresent(Consumer<? super T> action) {
    if (value != null) {
        action.accept(value);
    }
}
复制代码

E.g:

Optional.ofNullable(selectByOrderId(orderId)).ifPresent(o-> LOGGER.info("订单ID:{}",o.getOrderId());
复制代码

ifPresentOrElse () method

ifPresentOrElse()The method is JDK9 new method, which is ifPresent()an enhanced version of the method, if valuenot NULL, use the valueconsumer interface method calls a function of consumers Consumer#accept(), if valueto NULLexecute Runnable#run():

public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
    if (value != null) {
        action.accept(value);
    } else {
        emptyAction.run();
    }
}
复制代码

E.g:

String orderId = "xxxx"; 
Optional.ofNullable(selectByOrderId(orderId)).ifPresentOrElse(o-> LOGGER.info("订单ID:{}",o.getOrderId()), ()-> LOGGER.info("订单{}不存在",o.getOrderId()));
复制代码

filter () method

public Optional<T> filter(Predicate<? super T> predicate) {
    // 判断predicate不能为NULL
    Objects.requireNonNull(predicate);
    // value为NULL,说明是空实例,则直接返回自身
    if (!isPresent()) {
        return this;
    } else {
        // value不为NULL,则通过predicate判断,命中返回自身,不命中则返回空实例empty
        return predicate.test(value) ? this : empty();
    }
}
复制代码

This function is a simple method of filtering, the container holding the object valuenon NULL'll do a judge, he decided to return to their own instance or empty(). E.g:

Optional.ofNullable(selectByOrderId(orderId)).filter(o -> o.getStatus() == 1).ifPresent(o-> LOGGER.info("订单{}的状态为1",o.getOrderId));
复制代码

map () method

map()Value map is a simple operation:

public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    // 判断mapper不能为NULL
    Objects.requireNonNull(mapper);
    // value为NULL,说明是空实例,则直接返回empty()
    if (!isPresent()) {
        return empty();
    } else {
        // value不为NULL,通过mapper转换类型,重新封装为可空的Optional实例
        return Optional.ofNullable(mapper.apply(value));
    }
}
复制代码

An example API annotation inside:

List<URI> uris = ...;
// 找到URI列表中未处理的URI对应的路径
Optional<Path> p = uris.stream().filter(uri -> !isProcessedYet(uri)).findFirst().map(Paths::get);
复制代码

flatMap () method

flatMap()The method is a mapping operation, but the mapping Optionaltype of the return value is determined directly from the outside without going through the reseal value OptionalExample:

public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) {
    // mapper存在性判断
    Objects.requireNonNull(mapper);
    // value为NULL,说明是空实例,则直接返回empty()
    if (!isPresent()) {
        return empty();
    } else {
        // value不为NULL,通过mapper转换,直接返回mapper的返回值,做一次空判断
        @SuppressWarnings("unchecked")
        Optional<U> r = (Optional<U>) mapper.apply(value);
        return Objects.requireNonNull(r);
    }
}
复制代码

E.g:

class OptionalOrderFactory{

    static Optional<Order> create(String id){
        //省略...
    }
}

String orderId = "xxx";
Optional<Order> op =  Optional.of(orderId).flatMap(id -> OptionalOrderFactory.create(id));
复制代码

or () method

public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) {
    // supplier存在性判断
    Objects.requireNonNull(supplier);
    // value不为NULL,则直接返回自身
    if (isPresent()) {
        return this;
    } else {
        // value为NULL,则返回supplier提供的Optional实例,做一次空判断
        @SuppressWarnings("unchecked")
        Optional<T> r = (Optional<T>) supplier.get();
        return Objects.requireNonNull(r);
    }
}
复制代码

E.g:

Order a = null;
Order b = select();
// 拿到的就是b订单实例包装的Optional
Optional<Order> op = Optional.ofNullable(a).or(b);
复制代码

stream () method

// 对value做NULL判断,转换为Stream类型
public Stream<T> stream() {
    if (!isPresent()) {
        return Stream.empty();
    } else {
        return Stream.of(value);
    }
}
复制代码

orElse () method

// 值不为NULL则直接返回value,否则返回other
public T orElse(T other) {
    return value != null ? value : other;
}
复制代码

orElse()Is a common method to provide default values ​​reveal all the details, such as:

String v1 = null;
String v2 = "default";
// 拿到的就是v2对应的"default"值
String value = Optional.ofNullable(v1).orElse(v2);
复制代码

orElseGet () method

// 值不为NULL则直接返回value,否则返回Supplier#get()
public T orElseGet(Supplier<? extends T> supplier) {
    return value != null ? value : supplier.get();
}
复制代码

orElseGet()Only orElse()method an upgraded version, for example:

String v1 = null;
Supplier<String> v2 = () -> "default";
// 拿到的就是v2对应的"default"值
String value = Optional.ofNullable(v1).orElseGet(v2);
复制代码

orElseThrow () method

// 如果值为NULL,则抛出NoSuchElementException,否则直接返回value
public T orElseThrow() {
    if (value == null) {
        throw new NoSuchElementException("No value present");
    }
    return value;
}

// 如果值不为NULL,则直接返回value,否则返回Supplier#get()提供的异常实例
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
    if (value != null) {
        return value;
    } else {
        throw exceptionSupplier.get();
    }
}
复制代码

E.g:

Optional.ofNullable(orderInfoVo.getAmount()).orElseThrow(()-> new IllegalArgumentException(String.format("%s订单的amount不能为NULL",orderInfoVo.getOrderId())));
复制代码

equals () and hashCode () method

public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }

    if (!(obj instanceof Optional)) {
        return false;
    }

    Optional<?> other = (Optional<?>) obj;
    return Objects.equals(value, other.value);
}

public int hashCode() {
    return Objects.hashCode(value);
}
复制代码

Comparison of these two methods are valuedescribed of Optionalexample, if used in HashMapthe KEY, as long as valuethe same, for HashMapthat same KEY. Such as:

Map<Optional,Boolean> map = new HashMap<>();
Optional<String> op1 = Optional.of("throwable");
map.put(op1, true);
Optional<String> op2 = Optional.of("throwable");
map.put(op2, false);
// 输出false
System.out.println(map.get(op1));
复制代码

Optional combat

The following show how Optionalsome common usage scenarios.

Empty judgment

Empty judgment is mainly used do not know whether the current object is NULLwhen you need to set properties of the object. Not used Optionalwhen the code is as follows:

if(null != order){
    order.setAmount(orderInfoVo.getAmount());
}
复制代码

Using Optionalthe time code is as follows:

Optional.ofNullable(order).ifPresent(o -> o.setAmount(orderInfoVo.getAmount()));

// 如果判断空的对象是OrderInfoVo如下
Order o = select();
OrderInfoVo vo = ...
Optional.ofNullable(vo).ifPresent(v -> o.setAmount(v.getAmount()));
复制代码

Use Optionalthe benefits of empty judgment is achieved only when a property value set can be compressed into one line of code to do so, the code will be relatively simple.

Affirmation

In the maintenance of some old system, when an external mass participation in many cases there is no short judgment, it is necessary to write some code that assertions such as:

if (null == orderInfoVo.getAmount()){
    throw new IllegalArgumentException(String.format("%s订单的amount不能为NULL",orderInfoVo.getOrderId()));
}
if (StringUtils.isBlank(orderInfoVo.getAddress()){
    throw new IllegalArgumentException(String.format("%s订单的address不能为空",orderInfoVo.getOrderId()));
}
复制代码

Using Optionalthe assertion code is as follows:

Optional.ofNullable(orderInfoVo.getAmount()).orElseThrow(()-> new IllegalArgumentException(String.format("%s订单的amount不能为NULL",orderInfoVo.getOrderId())));
Optional.ofNullable(orderInfoVo.getAddress()).orElseThrow(()-> new IllegalArgumentException(String.format("%s订单的address不能为空",orderInfoVo.getOrderId())));
复制代码

Integrated Simulation Case

The following is a simulation case, the step of simulation are as follows:

  • Gives the customer ID list query list of customers.
  • Check your order list based on the list of clients that exist in the customer ID.
  • Converted to an order DTO view the list based on the order list.
@Data
static class Customer {

    private Long id;
}

@Data
static class Order {

    private Long id;
    private String orderId;
    private Long customerId;
}

@Data
static class OrderDto {

    private String orderId;
}

// 模拟客户查询
private static List<Customer> selectCustomers(List<Long> ids) {
    return null;
}

// 模拟订单查询
private static List<Order> selectOrders(List<Long> customerIds) {
    return null;
}

// main方法
public static void main(String[] args) throws Exception {
    List<Long> ids = new ArrayList<>();
    List<OrderDto> view = Optional.ofNullable(selectCustomers(ids))
            .filter(cs -> !cs.isEmpty())
            .map(cs -> selectOrders(cs.stream().map(Customer::getId).collect(Collectors.toList())))
            .map(orders -> {
                List<OrderDto> dtoList = new ArrayList<>();
                orders.forEach(o -> {
                    OrderDto dto = new OrderDto();
                    dto.setOrderId(o.getOrderId());
                    dtoList.add(dto);
                });
                return dtoList;
            }).orElse(Collections.emptyList());
}
复制代码

summary

OptionalIt is essentially an object container, which is characterized as follows:

  1. OptionalAs a container carrying an object to provide a method fitting portion interface function, binding portion provides interface functions implemented method of NULLdetermination, filtering, security values, mapping operations and so on.
  2. OptionalScenario is typically used for packaging method returns a value, of course, also be used as temporary variables and thus enjoy convenient functional interfaces.
  3. OptionalJust a tool to simplify the operation, not a silver bullet, there are many problems can not be solved substantive encoding, such as an arrow type code issues.

Arrow type the code mentioned here, try the following conventional methods and Optionalare resolved (no substantive change, but the introduction of a higher code complexity):

// 假设VO有多个层级,每个层级都不知道父节点是否为NULL,如下
// - OrderInfoVo
//   - UserInfoVo
//     - AddressInfoVo
//        - address(属性)
// 假设我要为address属性赋值,那么就会产生箭头型代码。


// 常规方法
String address = "xxx";
OrderInfoVo o = ...;
if(null != o){
    UserInfoVo uiv = o.getUserInfoVo();
    if (null != uiv){
        AddressInfoVo aiv = uiv.getAddressInfoVo();
        if (null != aiv){
            aiv.setAddress(address);
        }
    }
}

// 使用Optional
String address = "xxx";
OrderInfoVo o = ...;
Optional.ofNullable(o).ifPresent(oiv-> {
      Optional.ofNullable(oiv.getUserInfoVo()).ifPresent(uiv -> {
           Optional.ofNullable(uiv.getAddressInfoVo()).ifPresent(aiv->{
                aiv.setAddress(address);
           })
      })
});
复制代码

Some developers proposed the DAOmethod's return value type is defined as Optional, the author of this neutral, because:

  1. OptionalIs JDK1.8 introduced low version of the JDK and can not be used, not all systems can smooth migration to JDK1.8 +.
  2. Not everyone is keen on functional programming, because it brings convenience at the same time changed the logic of reading the code (some people even think that reduces the readability of the code).

annex

(End herein, c-2-d ea-20190805 by throwable)

Guess you like

Origin juejin.im/post/5d4b6d9c6fb9a06b155da925