代码之家  ›  专栏  ›  技术社区  ›  stratwine

以编程方式引发NullPointerException可以吗?

  •  41
  • stratwine  · 技术社区  · 15 年前

    当存在post条件时,方法的返回值不能为空,可以做什么?

    我能做到

    assert returnValue != null : "Not acceptable null value";
    

    但是断言可能会被关闭!

    这样可以吗

    if(returnValue==null)
          {
               throw new NullPointerException("return value is null at method AAA");
          }
    

    ?

    或者,对于这样的条件,最好使用用户定义的异常(如nullReturnValueException)?

    19 回复  |  直到 7 年前
        1
  •  40
  •   waxwing    15 年前

    我认为在JVM为您做之前尽早抛出一个NPE没有问题——特别是对于空参数。关于这一点似乎有一些争论,但是Java SE库中有很多例子确实做到了这一点。我不明白为什么NPE应该是神圣的方面,你不能扔它自己。

    然而,我离题了。这个问题与其他问题有关。您所说的是一个post条件,声明返回值不能为空。在这种情况下,肯定是空的,这意味着你有一个bug 在这个方法里面 ?

    你会怎么记录这个?”如果返回值意外为空,则此方法将引发NullPointerException“”。不解释这是怎么发生的?不,我会在这里使用断言。异常应该用于可以想象发生的错误-不包括方法中发生错误时可能发生的事情,因为这对任何人都没有帮助。

        2
  •  60
  •   Roman    12 年前

    我建议你不要扔 NullPointerException 一个人。

    正如托尔比安徒生在下面的评论中所说,不这样做的主要原因是,你不想把“真实的、坏的NPE”和有意抛出的NPE混为一谈。

    所以,在你确信你能识别出“有效”的NPE之前,我建议你使用 IllegalArgumentException 当你想告诉你的API用户 null 不是有效的参数值。应该记录传递非法空参数时方法的行为。

    另一种(更现代的imho)选择是使用 @NotNull 参数附近的注释。 这里是 an article about using @NotNull annotation .

    正如我前面提到的,也有一些情况,当投掷NPE时,无论对你还是你的队友都不会感到困惑:NPE的原因应该是清楚和可识别的。

    例如,如果使用带有preconditions模块的库,例如 Guava ,然后我发现使用 checkNotNull() -like方法是处理非法传递的空值的首选方法。

    checkNotNull(arg, msg) 抛出NPE,但从stacktrace可以很清楚地看出,它是由 Preconditions.checkNotNull() 因此,这不是一个未知的错误,而是预期的行为。

        3
  •  27
  •   Timo Geusch    15 年前

    鉴于 NullPointerException 是在Java中传递意外的空值的惯用方式,我建议您抛出一个标准 空指针异常 而不是国产的。还要记住,最不令人吃惊的原则是,对于存在系统异常类型的情况,您不会发明自己的异常类型。

    断言有利于调试,但如果必须处理某些条件,则不好,因此这不是处理错误条件的好方法。

        4
  •  12
  •   Cjxcz Odjcayrwl    12 年前

    问题在于 NullPointerException 是,当你 忘记 检查某个值是否为空或给出错误的参数,该参数为空且不应为空。

    根据我的经验,Java程序员很快就知道,这个异常是由代码中的错误引起的,所以手动抛出它将是极其混乱的。 他们当中。 IllegalArgumentException 当您传递不可接受的参数(如空值,其中某些值不能为空值)时,最好是这样。

    它还触发了另一个启发式的。npe=这里有人在代码中出错, 非法数据异常 =给方法的对象无效。

    另一方面,javadoc告诉我们:

    应用程序应引发此类的实例以指示
    其他非法使用 null 对象。

    所以投掷NPE将是 合法的 不过,这不是常见的做法,所以我建议 非法数据异常 .

        5
  •  7
  •   Affe    15 年前

    当然,没有一个通用的法律可以禁止抛出nullpointerException,但是如果在这样一个抽象的例子中您真的应该这样做,那么很难回答。你不想做的是把人们放到试图捕获nullpointerException的位置上。这样的代码(我发誓,真的是这样的例子):

    catch (NullPointerException npe) {
      if (npe.getMessage().equals("Null return value from getProdByCode") {
        drawToUser("Unable to find a product for the product type code you entered");
      } 
    }
    

    肯定是你做错了什么。因此,如果空返回值是您实际上能够通信的某个系统状态的指示器,那么使用一个异常来通信该状态。我能想到的情况不多,在哪里只需要检查一个引用来丢弃一个空指针就可以了。通常,下一行代码会把空指针(或更具信息性的东西)扔掉!

        6
  •  5
  •   Thorbjørn Ravn Andersen    15 年前

    我认为nullpointerException的用法可以, 如果 你记得描述。这就是调查人员的工作内容(行号可能会移动)。还请记住,在特殊情况下,您的方法会抛出空指针异常。

    如果在开始时检查方法参数, throw new IllegalArgumentException("foo==null") 我也能接受。

        7
  •  3
  •   meriton    15 年前

    这个 JavaDoc for NullPointerException 国家:

    当应用程序尝试 在对象为 必修的。这些包括:

    * Calling the instance method of a null object.
    * Accessing or modifying the field of a null object.
    * Taking the length of null as if it were an array.
    * Accessing or modifying the slots of null as if it were an array.
    * Throwing null as if it were a Throwable value. 
    

    应用程序应引发的实例 此类表示其他非法 空对象的使用。

    我认为违反岗位条件是非法行为。但是,我认为您使用的异常并不重要,因为我们讨论的是一个应该(并且希望是)不可访问的代码路径,因此您不会有特定于该异常的错误处理,因此该名称的唯一影响是日志文件中某些条目的不同措辞,没有人可能看到。

    相反,如果您认为post条件可能被违反,那么最好包含更多的调试信息,例如调用方法时使用的参数。

        8
  •  3
  •   Tim Bender    15 年前

    如果您描述的方法合同的返回值不能是 null 你最好不要回来 无效的 . 但这根本不是nullpointerException。如果您必须返回的值是 无效的 显然,来电者给了你不好的论据( IllegalArgumentException )您处于无效状态( IllegalStateException 或者发生了除NullPointerException(通常表示编程错误)以外的其他一些更有意义的异常情况。

        9
  •  3
  •   Gab    8 年前

    http://pmd.sourceforge.net/pmd-5.0.1/rules/java/strictexception.html
    “避免引发NullPointerExceptions。这些都令人困惑,因为大多数人都会认为是虚拟机抛出的。考虑改用IllegalArgumentException;这将明显视为程序员启动的异常。”

        10
  •  1
  •   Rafe Kettler    15 年前

    我给一本书打过电话 奥莱利的Java简言之 由专家编写,列出了nullpointerException的定义:

    表示试图访问字段或调用空对象的方法。

    因为返回空值不是这两种情况中的任何一种,所以我认为编写自己的异常更合适。

        11
  •  1
  •   user207421    8 年前

    在逻辑变得如此深入之前抛出NPE通常是一个很好的主意,调用程序员很难弄清楚什么是空的。addListener()方法就是一个很好的例子。

    尽管没有进行投票,JDK中有许多方法可以做到这一点。

        12
  •  0
  •   Jerod Houghtelling    15 年前

    在我看来,你不应该手动抛出NullPointerException。调用例程在不检查描述的情况下不知道实际或手动NullPointerException。在这种情况下,您可能希望滚动自己的异常,使其更接近问题,以便调用方法能够正确地恢复FRM这个异常。对于许多情况,后条件异常可能是足够通用的。

        13
  •  0
  •   CurtainDog    15 年前

    在JAVA诗句中,NULL在期望对象时总是有效的值。你最好避免这种不可能的岗位条件。如果您真的不能遵守一个空值,那么您将不得不重写您的方法,这样您就可以返回一个原语。

        14
  •  0
  •   Eugene    12 年前

    正如乔舒亚·布洛克曾经说过的:“空的,糟透了!”:)每当我的脸为空时,我会尝试使用guava提供的可选选项。我有很多优点。

        15
  •  0
  •   Jin Kwon    12 年前

    绝对可以。

    甚至JDK7也能解决这个问题。见 Objects#requireNonNull

    void doWith(final Object mustBeNotNull) {
    
        /*
        // bush style
        if (mustBeNotNull == null) {
            throw new IllegalArgumentException("mustBeNotNull must not be null");
        }
        */
    
        /*
        // obama style
        if (mustBeNotNull == null) {
            throw new NullPointerException("mustBeNotNull must not be null");
        }
        */
    
        // kangnam style
        Objects.requireNonNull(mustBeNotNull, "mustBeNotNull must not be null");
    
        assert mustBeNotNull != null;
    }
    
        16
  •  0
  •   Lumi    11 年前

    当存在post条件时,方法的返回值不能为空,可以做什么?

    post条件意味着,如果条件不满足,则所讨论的方法有一个bug。用代码来表示这一点的方法是使用 assert 在岗位条件下。直接引发异常,例如 NullPointerException IllegalStateException 会有点误导,因此也会误导。

    可以通过编程抛出NullPointerException吗?

    NPE的Java API文档说是的,但是,根据这个页面上的投票,3:1的开发者表示不同意,所以我认为这取决于工作组中的约定。

    API文档首先列出了JVM引发NPE的情况,因为代码试图在需要某种类型的对象(如调用方法或访问字段)的空引用上调用操作,并且 null 不是对象。然后陈述:

    应用程序应引发此类的实例,以指示其他非法使用 无效的 对象。

    有趣的是, 无效的 这里称为“对象”,而不是。这让我想起了这个名字 空指针异常 对于没有指针的语言来说很奇怪。(那应该是 NullReferenceException 就像在Microsoft.NET类库中一样。)

    那么,我们是否应该就这一点取消API文档?我不这么认为。类库确实使用文档中描述的NPE,例如 java.nio.channels :

    除非另有说明,通过 无效的 此包中任何类或接口中的构造函数或方法的参数将导致 空指针异常 被扔掉

    这不是由jvm生成的npe,而是一个带有附加错误消息的编码npe,说明哪个参数是 无效的 (像 "in" is null! )(可以通过执行以下操作看到代码 javap -c -p java.nio.channels.Channels | more 寻找 private static void checkNotNull 有许多类以这种方式使用NPE,本质上是作为 IllegalArgumentException .

    因此,在调查了这一点并思考之后,我发现这是对NPE的一个很好的利用,因此我同意API doc和少数Java开发人员(根据本页的投票),您有权和有权使用Java类库相同的方式在您自己的代码中使用NPE,也就是说提供错误的混乱。年龄,在JVM生成的NPE中明显缺失,这就是为什么区分这两种NPE没有问题的原因。

    为了解决NPE无论如何都会被扔到更远的道路上的一个小问题:尽早捕获错误很有意义,而不是让JVM继续执行程序,可能涉及磁盘或网络I/O(和延迟),并生成不必要的大堆栈跟踪。

        17
  •  0
  •   Erich    8 年前

    是的,没关系,但我认为这是个更好的决定 就让它发生吧 .

    Java程序员和NULL的问题是,人们来自C/C++背景,NULL意味着很多不同的东西。在C/C++中,一个空(或野生)指针是一个严重的问题,它会引起奇怪的内存问题或崩溃你的程序(显然不可取)。如果你能摆脱C/C++的思维方式,你会意识到你有一个额外的层,JVM,它为你处理这个条件,你开始对NULL的想法有点不同。

    在C++中,我们有引用,例如,它永远不能被赋值为null。在爪哇中,没有引用,但是Java对象参数的行为更像C++指针。但是在Java中有很多情况,其中一种方法隐含地应该 不是 接收参数的空值!那么,我们该怎么办?

    像在C++中那样处理Java中的NULL的问题是,这会导致空校验。 处处 而在C++中,您只需声明一个引用引用的方法,它明确声明它不接受NULL。很快,每个方法都必须在其中进行一次健全性检查,以断言程序的状态,从而造成混乱。

    假设一个方法的默认约定是参数值的空值无效,那么在这种情况下工作是一种更好的心态。

    为什么?好吧,让我们看看当这样的方法接收到空作为参数值时会发生什么。a)也许可以,因为它不会将值作为其行为的一部分取消引用。在这种情况下,什么都不会发生。b)该值被取消引用。在这种情况下,来自jvm的行为正是我们所希望的:抛出一个异常,指示由于参数值为空而违反了方法的约定,并且它包含一个堆栈跟踪,将我们一直带到方法中使用该值的行。

    人们对NPE持异议,因为他们认为当你在日志中看到NPE时,它意味着“某人搞砸了”。但让我们考虑一下。与预期行为相比,NPE作为一个糟糕的指标,到底有什么区别?我认为使用NPE作为预期行为的主要区别(和优势)是它不指向发生它的方法,而是指向违反方法契约的调用方。这是更有用的信息。如果我们只是简单地检查空值并抛出一个不同的异常,那么当调用方确实违反了方法的约定时,我们可能会误以为观察到的行为是一个预期错误。恭喜您,您正确地预料到了调用者在调用该方法时可能会搞砸的情况——不过,您所做的一切都使自己误会了异常的真正原因是什么——或者最多,您使用了两个不同的异常类来表示同一件事,并在Me中大量地使用不必要的垃圾来污损代码。安时。

    所以说到IT,人们认为NPE是禁忌。从字面上讲,人们不会允许它被抛出,因为它伴随着某种羞耻感——好像你不够聪明,因为你没能猜出值在哪里会是空的。好吧,我有消息告诉你们,你们只是在写更多无用的代码来做同样的事情。

    一些例子:

    public void foo(Object o) {
      if (o == null) {
        throw new IGotchaException("HA! You're an IDIOT! I knew it!!");
      }
      o.bar();
    }
    
    public void foo(Object o) {
      o.bar();
    }
    

    ^政治上不同。从功能上讲,没那么多。

    public void foo(int a, long b, double c, Object o) {
      if (o == null) {
        throw new IllegalArgumentException("Oh. Uh. Well, you've passed me null here. I... I'm not sure where to go from here because this object is kind of required for this function to do what it's supposed to do. Soooo... wouldja mind reworking your code a little bit so as to not pass null to this function as a parameter?! That'd be great thanks. Oh by the way, it's cause we deference o 40 lines below here");
      }
      // ...
      o.doSomethingWithA(a);
    }
    
    public void foo(int a, long b, double c, Object o) {
      // ...
      o.doSomethingWithA(a);
      // NullPointerException, line 40, e.g. it wasn't OK to pass null for o you lunkhead
    }
    

    ^也许可以节省一些CPU周期,代价是牺牲大量烦人的代码。然而,在第二种情况下,我们做的比较较少。

    public void foo(Object a, Object b, Object c, Object d) {
      if (a == null) throw IllegalArgumentException("jackass");
      if (b == null) throw IllegalArgumentException("jackass");
      if (c == null) throw IllegalArgumentException("jackass");
      // But d is totally OK!
      // ...
      c.setSomeValueThatMayBeNull(d);
    }
    
    public void foo(Object a, Object b, Object c, Object d) {
      // ...
      c.setSomeValueThatMayBeNull(d);
      // Throws null pointer exception if c is null, but not if d is null. Which is basically the same as above
    }
    

    ^契约是语句中的隐式契约,而不是方法开头的异常情况。没有其他缺点。

    public void foo(Object o) {
      if (o == null) {
        doTheBoogie();
      } else {
        doTheRobot();
      }
    }
    

    坏的

    public void foo(Object o, int b) {
      Bar value = o.findSomethingWhichMayExist(b);
      if (value == null)
        return;
      value.doSomething();
    }
    

    ^使用空返回值指示缺少值。好啊。

    人们对NPE有问题的另一个原因是他们不知道如何处理异常。NPE永远不应该是一个ShowStopper。适当的行为是捕获runtimeexception,可能是在调用堆栈中的更高(或更低,具体取决于您如何看到它)级别捕获它并在“main”之前报告它。也就是说,假设您正在开发一种需要更具弹性的程序,而不仅仅是 崩溃 当不良事件发生时。

    底线:不要期望为方法参数传入空值是一件有效的事情。并且绝对不要为显式接受空值并将其视为有效值的方法创建协定。但是, 允许 空指针异常会发生,让代码自然失败,或者在不重要的时候不失败。

        18
  •  0
  •   Peter S.    7 年前

    我同意前面答案中的声明,即NPE是代码中的错误,不应该抛出,但是开发人员应该修复意外的空值。然而,这种状态大多数时候可以通过测试来防止。

    这个问题在7年前被问及,但是现在我们在Java 8中有可选的,这个特性允许防止NPE。

    最后一个解决方案,在我的头脑中是,你应该检查对象是否为空,如果它是相等的,那么抛出你自己的异常来描述发生了什么。

        19
  •  -1
  •   Truong Ha    15 年前

    在某些情况下,抛出这个异常并不是一个好的实践,我想知道为什么当您已经用if语句捕获它时?

    if(返回值=空)