最佳实践:hasXXX()方法,用于返回可能的null返回getXXX()方法

时间:2009-09-08 13:09:55

标签: multithreading

这个问题可能看起来很简单,但我还没有找到答案,所以我问堆栈溢出社区。正如标题所示,我有一个带有几个getXXX()方法的类,其中一些方法可能返回null。这是记录在案的,这个类的用户应该知道这个事实。

为了简化这个类的用法,我有想法添加一些方便的hasXXX()方法,这些方法指示是否设置了特定字段。首先,这似乎是一个好主意......但随后会想到线程安全。

由于此类的实例可能跨线程共享,因此属性的值可能会更改。我们都知道check-then-act只有在我们知道在调用check-method之后状态不会改变时才有可能,即使我们在执行check-then-act之前被打断了。

我想到了以下解决方案:

  • 为此类用户提供一种方法,以便在执行check-then-act代码时“锁定”状态更改实例。
  • 删除hasXXX()方法,因为它们对可变类没用。

我不认为这是一个罕见的案例,一些SO成员可能在此之前发现了这个问题并找到了解决方案......

Foobaerchen

10 个答案:

答案 0 :(得分:10)

没有必要使问题复杂化 - 用户知道XXX是否未设置,因为getXXX()返回null。


if ( (x=bar.getXXX()) ) {
   x.foo();
}

是明确的


if ( bar.hasXXX() ) {
  bar.getXXX().foo();
}

留下了xXX对想象力的真正作用

答案 1 :(得分:5)

我绝对会删除hasXXX()方法。锁定事物会造成死锁的可能性,并且不容易纠正(更不用说它可能产生的性能问题)。

使用get方法并检查null是否有效,简单,快速,是一种众所周知的做事方式。消除NullPointerException s的可能性是一个有价值的原因,但通常是徒劳的。知道何时放弃它。

答案 2 :(得分:3)

  

check-then-act是唯一可行的   如果我们知道国家不会   检查方法结束后更改   调用,即使我们被打断了   在做我们的检查然后行动时。

即使您只有getXXX()种方法,也会遇到此问题:当线程A开始执行从getXXX()检索到的值时,线程B可能已经改变了这个领域。更糟糕的是,如果你必须getXXXgetYYY那么你可能会得到一个从不存在的事情的不一致视图,因为在两个调用之间对象都被更改了。

对于可能随时被其他线程更改的字段,您不应该使用getter。唯一的例外是当一个线程需要检查某些东西是否可用或已被另一个线程完成时,例如轮询用于线程之间通信的线程安全消息队列,或检查任务是否完成。

通常,将锁定限制为用于在线程之间交换信息的几个选择类的内部实现。不要在线程之间共享任何其他可变对象。

答案 3 :(得分:1)

了解使该对象不可变的方法,并在那些 hasXXX()方法中采用记录状态的方法。不确定这是否是一个选项,因为您可能已经在使用系统,但它也可以实现良好的OO实践。

答案 4 :(得分:1)

在500KLOC Java项目中,我们对可能返回null的方法使用了tryGetXXX()语法。每个字段都有明确的空权限,其名称使用yyyOrNull语法编码。然后使用静态分析工具Minik检查是否在不允许null的分支中执行对对象的访问。

不再有NPE问题:)

所有结构的不变性确保了线程安全。

http://www.mimuw.edu.pl/.../JC-TS-AGS-AS_Minik-A-tool-for-maintaining-proper-Java-code-structure.pdf http://ocean.comarch.com/genrap/

答案 5 :(得分:1)

boost::optional<T>捕获了C ++解决方案。这可以防止您从无意的空指针取消引用:编译器强制您明确处理boost::none情况。

答案 6 :(得分:0)

如果您的类的使用者知道如果未设置该值,则值将为null ...那么我不会担心提供{{​​1}}方法。

让消费者调用get方法,检查空值,然后让它们相应地处理它。

答案 7 :(得分:0)

如果null是一个完全有效的值,则使用hasXXX()方法只是合乎逻辑的。基本上,hasXXX()只能说它甚至没有返回的null值。如果hasXXX()仅用于检查getXXX()是否会返回null,那么你的逻辑就会出现问题,而且你需要来自Vulcan行星的人! ; - )

has / getXXX()组合的另一个问题是hasXXX()可能表明它有一个值,但另一个线程刚刚释放了数据,因此getXXX()将返回null。这将是一个严重的线程问题,你只能通过在整个has / getXXX()代码块周围添加一个锁来解决。

仍然,您可以通过创建一个组合函数来解决这个问题,您可以调用bigGetXXX(),它首先会设置一个锁,调用hasXXX()来检查一个值,然后调用getXXX()来检索该值。如果没有值,它将返回null,因此调用者必须检查值null ... 但是,这只是没有意义,因为您可以调用getXXX()来获取结果或立即返回null。 :-)或添加一个bighasXXX()方法,它将检查bigGetXXX()是否返回null。然后你将围绕这些创建一个新功能,这样可以再次保证线程安全。例如,verybiggetXXX()......你用这种方式获得的代码层数将无止境......

只需杀死hasXXX()方法。* 它们没有用处。

答案 8 :(得分:0)

1。 如果允许你的getXxx()方法返回null ,即使已经设置了成员(即显式空值),那么你需要一些其他方式告诉外面的世界成员已经确定。

我会返回一个特殊的常量值来表示“已设置但为空”。例如:

public class Foo
{
    public static final String  NULL_STRING = "Foo.NULL_STRING";

    private String  m_bar;

    public void setBar(String v)
    {
        if (v == null)
            v = NULL_STRING;
        m_bar = v;
    }

    public String getBar()
    {
        return m_bar;
    }
}

只要你没有实习生返回的字符串值,getBar()方法只会返回一个等于的值(即引用等于to {} NULL_STRING如果m_bar成员已明确设置为null。仅当成员已设置时,该方法才会返回null

或者,您可以反向使用该技术,以便在成员设置时返回特殊值(例如STRING_NOT_SET),然后返回{{1}如果该成员明确地设置为null

null

答案 9 :(得分:0)

引入hasXXX()有两个可能的原因:

  • 因为hasXXX()getXXX() != null更好地传达其意图,因此可能被认为更易读
  • 因为getXXX()方法可能很昂贵,有时只需要布尔hasXXX() - 信息

OTOH,如果您通常需要getXXX()的价值,hasXXX()毫无意义。