Java “?” Operator for checking null - What is it? (Not Ternary!)

前端 未结 14 748
心在旅途
心在旅途 2021-01-27 15:38

I was reading an article linked from a slashdot story, and came across this little tidbit:

Take the latest version of Java, which tries to make null-poi

相关标签:
14条回答
  • 2021-01-27 16:17

    It is possible to define util methods which solves this in an almost pretty way with Java 8 lambda.

    This is a variation of H-MANs solution but it uses overloaded methods with multiple arguments to handle multiple steps instead of catching NullPointerException.

    Even if I think this solution is kind of cool I think I prefer Helder Pereira's seconds one since that doesn't require any util methods.

    void example() {
        Entry entry = new Entry();
        // This is the same as H-MANs solution 
        Person person = getNullsafe(entry, e -> e.getPerson());    
        // Get object in several steps
        String givenName = getNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.getGivenName());
        // Call void methods
        doNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.nameIt());        
    }
    
    /** Return result of call to f1 with o1 if it is non-null, otherwise return null. */
    public static <R, T1> R getNullsafe(T1 o1, Function<T1, R> f1) {
        if (o1 != null) return f1.apply(o1);
        return null; 
    }
    
    public static <R, T0, T1> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, R> f2) {
        return getNullsafe(getNullsafe(o0, f1), f2);
    }
    
    public static <R, T0, T1, T2> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Function<T2, R> f3) {
        return getNullsafe(getNullsafe(o0, f1, f2), f3);
    }
    
    
    /** Call consumer f1 with o1 if it is non-null, otherwise do nothing. */
    public static <T1> void doNullsafe(T1 o1, Consumer<T1> f1) {
        if (o1 != null) f1.accept(o1);
    }
    
    public static <T0, T1> void doNullsafe(T0 o0, Function<T0, T1> f1, Consumer<T1> f2) {
        doNullsafe(getNullsafe(o0, f1), f2);
    }
    
    public static <T0, T1, T2> void doNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Consumer<T2> f3) {
        doNullsafe(getNullsafe(o0, f1, f2), f3);
    }
    
    
    class Entry {
        Person getPerson() { return null; }
    }
    
    class Person {
        Name getName() { return null; }
    }
    
    class Name {
        void nameIt() {}
        String getGivenName() { return null; }
    }
    
    0 讨论(0)
  • 2021-01-27 16:19

    I'm not sure this would even work; if, say, the person reference was null, what would the runtime replace it with? A new Person? That would require the Person to have some default initialization that you'd expect in this case. You may avoid null reference exceptions but you'd still get unpredictable behavior if you didn't plan for these types of setups.

    The ?? operator in C# might be best termed the "coalesce" operator; you can chain several expressions and it will return the first that isn't null. Unfortunately, Java doesn't have it. I think the best you could do is use the ternary operator to perform null checks and evaluate an alternative to the entire expression if any member in the chain is null:

    return person == null ? "" 
        : person.getName() == null ? "" 
            : person.getName().getGivenName();
    

    You could also use try-catch:

    try
    {
       return person.getName().getGivenName();
    }
    catch(NullReferenceException)
    {
       return "";
    }
    
    0 讨论(0)
  • 2021-01-27 16:22

    The original idea comes from groovy. It was proposed for Java 7 as part of Project Coin: https://wiki.openjdk.java.net/display/Coin/2009+Proposals+TOC (Elvis and Other Null-Safe Operators), but hasn't been accepted yet.

    The related Elvis operator ?: was proposed to make x ?: y shorthand for x != null ? x : y, especially useful when x is a complex expression.

    0 讨论(0)
  • 2021-01-27 16:24

    Since Android does not support Lambda Functions unless your installed OS is >= 24, we need to use reflection.

    // Example using doIt function with sample classes
    public void Test() {
        testEntry(new Entry(null));
        testEntry(new Entry(new Person(new Name("Bob"))));
    }
    
    static void testEntry(Entry entry) {
        doIt(doIt(doIt(entry,  "getPerson"), "getName"), "getName");
    }
    
    // Helper to safely execute function 
    public static <T,R> R doIt(T obj, String methodName) {
        try {
           if (obj != null) 
               return (R)obj.getClass().getDeclaredMethod(methodName).invoke(obj);
        } catch (Exception ignore) {
        }
        return null;
    }
    
    // Sample test classes
        static class Entry {
            Person person;
            Entry(Person person) { this.person = person; }
            Person getPerson() { return person; }
        }
    
        static class Person {
            Name name;
            Person(Name name) { this.name = name; }
            Name getName() { return name; }
        }
    
        static class Name {
            String name;
            Name(String name) { this.name = name; }
            String getName() {
                System.out.print(" Name:" + name + " ");
                return name;
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-27 16:25

    One way to workaround the lack of "?" operator using Java 8 without the overhead of try-catch (which could also hide a NullPointerException originated elsewhere, as mentioned) is to create a class to "pipe" methods in a Java-8-Stream style.

    public class Pipe<T> {
        private T object;
    
        private Pipe(T t) {
            object = t;
        }
    
        public static<T> Pipe<T> of(T t) {
            return new Pipe<>(t);
        }
    
        public <S> Pipe<S> after(Function<? super T, ? extends S> plumber) {
            return new Pipe<>(object == null ? null : plumber.apply(object));
        }
    
        public T get() {
            return object;
        }
    
        public T orElse(T other) {
            return object == null ? other : object;
        }
    }
    

    Then, the given example would become:

    public String getFirstName(Person person) {
        return Pipe.of(person).after(Person::getName).after(Name::getGivenName).get();
    }
    

    [EDIT]

    Upon further thought, I figured out that it is actually possible to achieve the same only using standard Java 8 classes:

    public String getFirstName(Person person) {
        return Optional.ofNullable(person).map(Person::getName).map(Name::getGivenName).orElse(null);
    }
    

    In this case, it is even possible to choose a default value (like "<no first name>") instead of null by passing it as parameter of orElse.

    0 讨论(0)
  • 2021-01-27 16:26

    You can test the code which you have provided and it will give syntax error.So, it is not supported in Java. Groovy does support it and it was proposed for Java 7 (but never got included).

    However, you can use the Optional provided in Java 8. This might help you in achieving something on similar line. https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

    Example Code for Optional

    0 讨论(0)
提交回复
热议问题