Please, don't use it anymore! =null is null!

△Hollis, a person with a unique pursuit of Coding△

This is Hollis's  307th  original sharing

Author l God loves apples

Source l Hollis (ID: hollischuang)

For Java programmers, null is a headache. It is often harassed by the Null Pointer Exception (NPE). Even the inventor of Java admitted that this was a huge mistake of his.

So, is there any way to avoid writing a large number of null statements in the code?

Some people say that you can use the Optional provided by JDK8 to avoid null, but it is still a bit troublesome to use.

In his daily work, the author encapsulates a tool that can call object members in a chain without the need to judge empty. Compared with the original if null logic and the Optional provided by JDK8, it is more elegant and easy to use, and the coding efficiency is greatly improved in engineering practice. , Which also makes the code more precise and elegant.

Not elegant for air conditioning

I think my friends who are engaged in Java development must have encountered the following uncomfortable null logic: Now there is a User class, and School is its member variable

/**

* @author Axin

* @since 2020-09-20

* @summary 一个User类定义

 * (Ps:Data 是lombok组件提供的注解,简化了get set等等的约定代码)

*/

@Data

public class User {

    private String name;

    private String gender;

    private School school;

    @Data

    public static class School {

        private String scName;

        private String adress;

    }

}

Now I want to get the member variable adress of School, the general processing method:

public static void main(String[] args) {

    User axin = new User();

    User.School school = new User.School();

    axin.setName("hello");

    if (Objects.nonNull(axin) && Objects.nonNull(axin.getSchool())) {

        User.School userSc = axin.getSchool();

        System.out.println(userSc.getAdress());

    }

}

When obtaining adress, it is necessary to judge the school. Although it is a little troublesome, it can be used. The Optional tool provided by JDK8 is also possible, but it is still a bit troublesome.

And the following OptionalBean provides a method that can continuously call member variables in a chain without determining nulls, and directly chain calls to the target variable you want to obtain without worrying about the issue of null pointers.

Chain call member variable

If you use the tool OptionalBean designed in this article, the above call can be simplified to this:

public static void main(String[] args) {

    User axin = new User();

    User.School school = new User.School();

    axin.setName("hello");

    // 1. 基本调用

    String value1 = OptionalBean.ofNullable(axin)

            .getBean(User::getSchool)

            .getBean(User.School::getAdress).get();

    System.out.println(value1);

}

Results of the:

The school variable of User is empty, you can see that the code does not have a null pointer, but returns null. How does this tool work?

OptionalBean tool

/**

* @author Axin

* @since 2020-09-10

* @summary 链式调用 bean 中 value 的方法

*/

public final class OptionalBean<T> {

    private static final OptionalBean<?> EMPTY = new OptionalBean<>();

    private final T value;

    private OptionalBean() {

        this.value = null;

    }

    /**

     * 空值会抛出空指针

     * @param value

     */

    private OptionalBean(T value) {

        this.value = Objects.requireNonNull(value);

    }

    /**

     * 包装一个不能为空的 bean

     * @param value

     * @param <T>

     * @return

     */

    public static <T> OptionalBean<T> of(T value) {

        return new OptionalBean<>(value);

    }

    /**

     * 包装一个可能为空的 bean

     * @param value

     * @param <T>

     * @return

     */

    public static <T> OptionalBean<T> ofNullable(T value) {

        return value == null ? empty() : of(value);

    }

    /**

     * 取出具体的值

     * @param fn

     * @param <R>

     * @return

     */

    public T get() {

        return Objects.isNull(value) ? null : value;

    }

    /**

     * 取出一个可能为空的对象

     * @param fn

     * @param <R>

     * @return

     */

    public <R> OptionalBean<R> getBean(Function<? super T, ? extends R> fn) {

        return Objects.isNull(value) ? OptionalBean.empty() : OptionalBean.ofNullable(fn.apply(value));

    }

    /**

     * 如果目标值为空 获取一个默认值

     * @param other

     * @return

     */

    public T orElse(T other) {

        return value != null ? value : other;

    }

    /**

     * 如果目标值为空 通过lambda表达式获取一个值

     * @param other

     * @return

     */

    public T orElseGet(Supplier<? extends T> other) {

        return value != null ? value : other.get();

    }

    /**

     * 如果目标值为空 抛出一个异常

     * @param exceptionSupplier

     * @param <X>

     * @return

     * @throws X

     */

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

        if (value != null) {

            return value;

        } else {

            throw exceptionSupplier.get();

        }

    }

    public boolean isPresent() {

        return value != null;

    }

    public void ifPresent(Consumer<? super T> consumer) {

        if (value != null)

            consumer.accept(value);

    }

    @Override

    public int hashCode() {

        return Objects.hashCode(value);

    }

    /**

     * 空值常量

     * @param <T>

     * @return

     */

    public static<T> OptionalBean<T> empty() {

        @SuppressWarnings("unchecked")

        OptionalBean<T> none = (OptionalBean<T>) EMPTY;

        return none;

    }

}

The tool design mainly refers to the implementation of Optional, plus the extension of chain calls is the aforementioned OptionalBean.

getBean actually returns an OptionalBean object that wraps the empty value when the variable is empty, and the use of generics makes the tool easier to use.

manual

You can see that the code also provides the same extension methods as Optional, such as ifPresent(), orElse(), etc.:

public static void main(String[] args) {

    User axin = new User();

    User.School school = new User.School();

    axin.setName("hello");

    // 1. 基本调用

    String value1 = OptionalBean.ofNullable(axin)

            .getBean(User::getSchool)

            .getBean(User.School::getAdress).get();

    System.out.println(value1);

    // 2. 扩展的 isPresent方法 用法与 Optional 一样

    boolean present = OptionalBean.ofNullable(axin)

            .getBean(User::getSchool)

            .getBean(User.School::getAdress).isPresent();

    System.out.println(present);

    // 3. 扩展的 ifPresent 方法

    OptionalBean.ofNullable(axin)

            .getBean(User::getSchool)

            .getBean(User.School::getAdress)

            .ifPresent(adress -> System.out.println(String.format("地址存在:%s", adress)));

    // 4. 扩展的 orElse

    String value2 = OptionalBean.ofNullable(axin)

            .getBean(User::getSchool)

            .getBean(User.School::getAdress).orElse("家里蹲");

    System.out.println(value2);

    // 5. 扩展的 orElseThrow

    try {

        String value3 = OptionalBean.ofNullable(axin)

                .getBean(User::getSchool)

                .getBean(User.School::getAdress).orElseThrow(() -> new RuntimeException("空指针了"));

    } catch (Exception e) {

        System.out.println(e.getMessage());

    }

}

Run it:

to sum up

Designed a tool that can call object members in a chain without making the code more precise and elegant. If the tool designed in this article satisfies your problem, use it in your project!

If you have a better design or there are errors in the text, please leave a message to discuss together and improve each other!

—————END—————喜欢本文的朋友,欢迎关注公众号 程序员小灰,收看更多精彩内容点个[在看],是对小灰最大的支持!

Guess you like

Origin blog.csdn.net/bjweimengshu/article/details/109252282