Warm tip: This article is reproduced from serverfault.com, please click

what wrong with java lambda :: expression ? why ? what diff between :: and normal expression?

发布于 2020-12-02 02:20:57
public static void main(String[] args) {
    Manager manager1 = new Manager();
    Manager manager2 = null;

    // both line 1 and line 2 are ok
    NullUtils.objectNullFieldNull(manager1, () -> manager1.getName(), "");
    NullUtils.objectNullFieldNull(manager1, manager1::getName, "");

    // line 1 is ok but line 2 will throw NullPointerException
    NullUtils.objectNullFieldNull(manager2, () -> manager2.getName(), "");
    NullUtils.objectNullFieldNull(manager2, manager2::getName, "");
}

As we know, we can use xxx::yyy instead of () -> xxx.yyy() in Java, but if xxx is null, they will be different.

The version with :: will throw a NullPointerException, but () -> xxx.yyy() not throw an exception until the function is actually called.

Why is this?

I use IntelliJ IDEA to code, and if the code can use :: instead of ->, then the IDE will show a warning, telling me to change it to use :: (even though that would cause a NullPointerException).

How do I resolve this?

Other related code:

public class NullUtils {

    public static <O, R> R objectNullFieldNull(O o, Supplier<R> supplier, R orv) {
        return Objects.isNull(o) ? orv : objectNull(supplier.get(), orv);
    }

    public static <O> O objectNull(O original, O target) {
        return Objects.isNull(original) ? target : original;
    }
}
@Data
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
public class Manager {

    private String code;

    private String name;

    private String mobile;
}
Questioner
page xu
Viewed
0
khelwood 2020-12-02 20:24:44

As you have seen, if xxx is null, then () -> xxx.yyy() creates a function that will throw an NPE if it is called, but does not throw an exception if the function is created and not called. But xxx::yyy throws an NPE immediately. So if you are choosing between the two and don't want to throw an NPE, use the -> version.

However, there's another option. Your objectNullFieldNull method is somewhat confusingly written, because the object passed as the first argument has no clear relationship with the supplier passed as the second argument. It would seem more natural (to me) for the second argument to be a function using the first, like this:

public static <O, R> R objectNullFieldNull(O o, Function<? super O, R> field, R orv) {
    return o==null ? orv : objectNull(field.apply(o), orv);
}

You can make use of it like this:

objectNullFieldNull(manager2, Manager::getName, "")

or like this

objectNullFieldNull(manager2, m -> m.getName(), "")

and neither will NPE if manager2 is null.

Manager::getName is a Function<Manager, String> that accepts a manager (or null) as its argument, and attempts to call getName() on it. See method references: Reference to an instance method of an arbitrary object of a particular type.