检查“get”链要求为空

发布于 2024-09-14 01:37:33 字数 486 浏览 5 评论 0原文

假设我想执行以下命令:

house.getFloor(0).getWall(WEST).getDoor().getDoorknob();

为了避免 NullPointerException,我必须执行以下操作:

if (house != null && house.getFloor(0) && house.getFloor(0).getWall(WEST) != null
  && house.getFloor(0).getWall(WEST).getDoor() != null) ...

是否有一种方法或现有的 Utils 类可以更优雅地执行此操作,让我们说类似以下内容?

checkForNull(house.getFloor(0).getWall(WEST).getDoor().getDoorknob());

Let's say I'd like to perform the following command:

house.getFloor(0).getWall(WEST).getDoor().getDoorknob();

To avoid a NullPointerException, I'd have to do the following if:

if (house != null && house.getFloor(0) && house.getFloor(0).getWall(WEST) != null
  && house.getFloor(0).getWall(WEST).getDoor() != null) ...

Is there a way or an already existing Utils class that does this more elegantly, let's say something like the following?

checkForNull(house.getFloor(0).getWall(WEST).getDoor().getDoorknob());

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

红颜悴 2024-09-21 01:37:33

如果您无法避免违反所选答案中所述的德墨忒尔定律 (LoD),并且Java 8 介绍 可选,这可能是处理像您这样的获取链中的空值的最佳实践。

Optional 类型将使您能够连续传输多个映射操作(其中包含 get 调用)。空检查会在后台自动处理。

例如,当对象未初始化时,不会执行 print() 操作,也不会抛出任何异常。这一切我们都可以在幕后温和地处理。当对象初始化时,将进行打印。

System.out.println("----- Not Initialized! -----");

Optional.ofNullable(new Outer())
        .map(out -> out.getNested())
        .map(nest -> nest.getInner())
        .map(in -> in.getFoo())
        .ifPresent(foo -> System.out.println("foo: " + foo)); //no print

System.out.println("----- Let's Initialize! -----");

Optional.ofNullable(new OuterInit())
        .map(out -> out.getNestedInit())
        .map(nest -> nest.getInnerInit())
        .map(in -> in.getFoo())
        .ifPresent(foo -> System.out.println("foo: " + foo)); //will print!

class Outer {
    Nested nested;
    Nested getNested() {
        return nested;
    }
}
class Nested {
    Inner inner;
    Inner getInner() {
        return inner;
    }
}
class Inner {
    String foo = "yeah!";
    String getFoo() {
        return foo;
    }
}

class OuterInit {
    NestedInit nested = new NestedInit();
    NestedInit getNestedInit() {
        return nested;
    }
}
class NestedInit {
    InnerInit inner = new InnerInit();
    InnerInit getInnerInit() {
        return inner;
    }
}
class InnerInit {
    String foo = "yeah!";
    String getFoo() {
        return foo;
    }
}

因此,使用您的 getters 链,它将如下所示:

Optional.ofNullable(house)
        .map(house -> house.getFloor(0))
        .map(floorZero -> floorZero.getWall(WEST))
        .map(wallWest -> wallWest.getDoor())
        .map(door -> wallWest.getDoor())

它的返回将类似于 Optional ,这将使您的工作更加安全,而不必担心 null 异常。

In case you can't avoid breaking Law of Demeter (LoD) as stated in the chosen answer, and with Java 8 introducing Optional, it would be probably the best practice to handle nulls in chains of gets such as yours.

The Optional type will enable you to pipe multiple map operations (which contain get calls) in a row. Null checks are automatically handled under the hood.

For example, when the objects aren't initialized, no print() will be made and no Exceptions will be thrown. It all we be handled gently under the hood. When objects are initialized, a print will be made.

System.out.println("----- Not Initialized! -----");

Optional.ofNullable(new Outer())
        .map(out -> out.getNested())
        .map(nest -> nest.getInner())
        .map(in -> in.getFoo())
        .ifPresent(foo -> System.out.println("foo: " + foo)); //no print

System.out.println("----- Let's Initialize! -----");

Optional.ofNullable(new OuterInit())
        .map(out -> out.getNestedInit())
        .map(nest -> nest.getInnerInit())
        .map(in -> in.getFoo())
        .ifPresent(foo -> System.out.println("foo: " + foo)); //will print!

class Outer {
    Nested nested;
    Nested getNested() {
        return nested;
    }
}
class Nested {
    Inner inner;
    Inner getInner() {
        return inner;
    }
}
class Inner {
    String foo = "yeah!";
    String getFoo() {
        return foo;
    }
}

class OuterInit {
    NestedInit nested = new NestedInit();
    NestedInit getNestedInit() {
        return nested;
    }
}
class NestedInit {
    InnerInit inner = new InnerInit();
    InnerInit getInnerInit() {
        return inner;
    }
}
class InnerInit {
    String foo = "yeah!";
    String getFoo() {
        return foo;
    }
}

So, with your getters chain it will look like this:

Optional.ofNullable(house)
        .map(house -> house.getFloor(0))
        .map(floorZero -> floorZero.getWall(WEST))
        .map(wallWest -> wallWest.getDoor())
        .map(door -> wallWest.getDoor())

The return of it will be something like Optional<Door> which will allow you much safer work without worrying of null exceptions.

奈何桥上唱咆哮 2024-09-21 01:37:33

为了检查获取链是否为 null,您可能需要从闭包调用代码。闭包调用代码将如下所示:

public static <T> T opt(Supplier<T> statement) {       
    try {
        return statement.get();
    } catch (NullPointerException exc) {
        return null;
    }   
}

您可以使用以下语法调用它:

Doorknob knob = opt(() -> house.getFloor(0).getWall(WEST).getDoor().getDoorknob());

该代码也是类型安全的,并且通常按预期工作:

  1. 如果链中的所有对象都不 空。
  2. 如果链中的任何对象为 null,则返回 null

您可以将 opt 方法放入共享 util 类中,并在应用程序中的任何地方使用它。

In order to check a chain of gets for null you may need to call your code from a closure. The closure call code will look like this:

public static <T> T opt(Supplier<T> statement) {       
    try {
        return statement.get();
    } catch (NullPointerException exc) {
        return null;
    }   
}

And you call it using the following syntax:

Doorknob knob = opt(() -> house.getFloor(0).getWall(WEST).getDoor().getDoorknob());

This code is also type safe and in general works as intended:

  1. Returns an actual value of the specified type if all the objects in the chain are not null.
  2. Returns null if any of the objects in the chain are null.

You may place opt method into shared util class and use it everywhere in your application.

聚集的泪 2024-09-21 01:37:33

最好的方法是避免链条。如果您不熟悉迪米特定律 (LoD),我认为您应该熟悉。您给出了一个完美的消息链示例,该消息链与类过于密切,它没有必要了解任何内容。

德墨忒尔定律:http://en.wikipedia.org/wiki/Law_of_Demeter

The best way would be to avoid the chain. If you aren't familiar with the Law of Demeter (LoD), in my opinion you should. You've given a perfect example of a message chain that is overly intimate with classes that it has no business knowing anything about.

Law of Demeter: http://en.wikipedia.org/wiki/Law_of_Demeter

彩扇题诗 2024-09-21 01:37:33

当然,您可以简单地将整个表达式包装在 try-catch 块中,但这是一个坏主意。更简洁的是空对象模式。这样,如果你的房子没有 0 层,它只会返回一个像普通楼层一样的楼层,但没有实际内容;当楼层被要求提供它们没有的墙时,会返回类似的“空”墙等。

You could of course simply wrap the whole expression up in a try-catch block, but that's a bad idea. Something cleaner is the Null object pattern. With that, if your house doesn't have floor 0, it just returns a Floor that acts like a regular Floor, but has no real content; Floors, when asked for Walls they don't have, return similar "Null" Walls, etc, down the line.

堇色安年 2024-09-21 01:37:33

确保逻辑上不能为 null 的内容并非如此。例如 - 房子总是有一面西墙。为了避免状态中出现此类异常,您可以使用方法来检查您期望的状态是否存在:

if (wall.hasDoor()) {
   wall.getDoor().etc();
}

这本质上是空检查,但可能并不总是如此。

重点是,如果您有 null,您应该采取一些措施。例如 - return 或抛出 IllegalStateException

而你不应该做的 - 不要捕获 NullPointerException。运行时异常不适合捕获 - 不希望您能够从中恢复,依赖逻辑流的异常也不是一个好的实践。想象一下,您实际上并不希望某些内容为 null,并且您捕获(并记录)一个 NullPointerException。这不会是非常有用的信息,因为此时很多东西都可能为 null

Make sure things that can't logically be null are not. For example - a house always has a West wall. In order to avoid such exceptions in state, you can have methods to check whether the state you expect is present:

if (wall.hasDoor()) {
   wall.getDoor().etc();
}

This is essentially a null-check, but might not always be.

The point is that you should do something in case you have a null. For example - return or throw an IllegalStateException

And what you shouldn't do - don't catch NullPointerException. Runtime exceptions are not for catching - it is not expected that you can recover from them, nor it is a good practice to rely on exceptions for the logic flow. Imagine that you actually don't expect something to be null, and you catch (and log) a NullPointerException. This will not be very useful information, since many things can be null at that point.

对你而言 2024-09-21 01:37:33

对我来说更好的解决方案是使用 java.util.Optional.map(..) 链接这些检查: https://stackoverflow.com /a/67216752/1796826

Better solution for me is to use java.util.Optional.map(..) to chain these checks : https://stackoverflow.com/a/67216752/1796826

药祭#氼 2024-09-21 01:37:33

您无法编写任何 checkForNull 方法来实现此目的(这根本不是 Java 中方法调用和参数求值的工作方式)。

您可以将链接语句分解为多个语句,并在每一步进行检查。然而,也许更好的解决方案是首先不让这些方法返回 null。您可能想使用一种名为 空对象模式 的东西。

相关问题

There is no checkForNull method that you can write that will facilitate this (that's simply not how method invokation and argument evaluation works in Java).

You can break down the chained statements into multiple statements, checking at every step. However, perhaps a better solution is to not have these methods return null in the first place. There is something called the Null Object Pattern that you may want to use instead.

Related questions

伴我心暖 2024-09-21 01:37:33

您可能有一个如下所示的通用方法:

public static <T> void ifPresentThen(final Supplier<T> supplier, final Consumer<T> consumer) {
    T value;
    try {
        value = supplier.get();
    } catch (NullPointerException e) {
        // Don't consume "then"
        return;
    }
    consumer.accept(value);
}

所以现在您可以这样做

ifPresentThen(
    () -> house.getFloor(0).getWall(WEST).getDoor().getDoorknob(),
    doorKnob -> doSomething());

You could potentially have a generic method like below:

public static <T> void ifPresentThen(final Supplier<T> supplier, final Consumer<T> consumer) {
    T value;
    try {
        value = supplier.get();
    } catch (NullPointerException e) {
        // Don't consume "then"
        return;
    }
    consumer.accept(value);
}

So now you would be able to do

ifPresentThen(
    () -> house.getFloor(0).getWall(WEST).getDoor().getDoorknob(),
    doorKnob -> doSomething());
黑寡妇 2024-09-21 01:37:33

使用供应商实现 nullPointer try/catch,您可以将所有 get 链发送给它

public static <T> T getValue(Supplier<T> getFunction, T defaultValue) {
    try {
        return getFunction.get();
    } catch (NullPointerException ex) {
        return defaultValue;
    }
}

,然后以这种方式调用它。

ObjectHelper.getValue(() -> object1.getObject2().getObject3().getObject4()));

implementing nullPointer try/catch with a Supplier you can send it all chain of get

public static <T> T getValue(Supplier<T> getFunction, T defaultValue) {
    try {
        return getFunction.get();
    } catch (NullPointerException ex) {
        return defaultValue;
    }
}

and then call it in this way.

ObjectHelper.getValue(() -> object1.getObject2().getObject3().getObject4()));
清秋悲枫 2024-09-21 01:37:33
// Example
LazyObject.from(curr).apply(A.class, A::getB).apply(B.class, B::getC).apply(C.class, C::getD).to(String.class);

// LazyObject.java
public class LazyObject {

private Object value;

private LazyObject(Object object) {
    this.value = object;
}

public <F, T> LazyObject apply(Class<F> type, Function<F, T> func) {
    Object v = value;
    if (type.isInstance(v)) {
        value = func.apply(type.cast(v));
    } else {
        value = null; // dead here
    }
    return this;
}

public <T> void accept(Class<T> type, Consumer<T> consumer) {
    Object v = value;
    if (type.isInstance(v)) {
        consumer.accept(type.cast(v));
    }
}

public <T> T to(Class<T> type) {
    Object v = value;
    if (type.isInstance(v)) {
        return type.cast(v);
    }
    return null;
}

public static LazyObject from(Object object) {
    return new LazyObject(object);
}

}
// Example
LazyObject.from(curr).apply(A.class, A::getB).apply(B.class, B::getC).apply(C.class, C::getD).to(String.class);

// LazyObject.java
public class LazyObject {

private Object value;

private LazyObject(Object object) {
    this.value = object;
}

public <F, T> LazyObject apply(Class<F> type, Function<F, T> func) {
    Object v = value;
    if (type.isInstance(v)) {
        value = func.apply(type.cast(v));
    } else {
        value = null; // dead here
    }
    return this;
}

public <T> void accept(Class<T> type, Consumer<T> consumer) {
    Object v = value;
    if (type.isInstance(v)) {
        consumer.accept(type.cast(v));
    }
}

public <T> T to(Class<T> type) {
    Object v = value;
    if (type.isInstance(v)) {
        return type.cast(v);
    }
    return null;
}

public static LazyObject from(Object object) {
    return new LazyObject(object);
}

}
泪冰清 2024-09-21 01:37:33

非常老的问题,但仍然添加我的建议:

我建议不要在一个方法调用链中从房子深处获取 DoorKnob,您应该尝试让 DoorKnob 从调用代码提供给此类,或者通过创建一个专门用于此目的的中央查找设施(例如 DoorKnob 服务)

松散耦合设计的简化示例:

class Architect {

    FloorContractor floorContractor;

    void build(House house) {
        for(Floor floor: house.getFloors()) {
            floorContractor.build(floor);
        }
    }    
}

class FloorContractor {

    DoorMaker doorMaker;

    void build(Floor floor) {
        for(Wall wall: floor.getWalls()) {
            if (wall.hasDoor()) {
                doorMaker.build(wall.getDoor());
            }
        }
    } 
}

class DoorMaker {

    Tool tool;

    void build(Door door) {
        tool.build(door.getFrame());
        tool.build(door.getHinges());
        tool.build(door.getDoorKnob());
    }        
}

Very old question, but still adding my suggestion:

I would suggest instead of getting the DoorKnob from deep within the House in one method call chain, you should try to let the DoorKnob be provided to this class from the calling code, or by creating a central lookup facility specifically for this purpose (e.g. a DoorKnob service)

Simplified example of design with loose coupling:

class Architect {

    FloorContractor floorContractor;

    void build(House house) {
        for(Floor floor: house.getFloors()) {
            floorContractor.build(floor);
        }
    }    
}

class FloorContractor {

    DoorMaker doorMaker;

    void build(Floor floor) {
        for(Wall wall: floor.getWalls()) {
            if (wall.hasDoor()) {
                doorMaker.build(wall.getDoor());
            }
        }
    } 
}

class DoorMaker {

    Tool tool;

    void build(Door door) {
        tool.build(door.getFrame());
        tool.build(door.getHinges());
        tool.build(door.getDoorKnob());
    }        
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文