将 null 传递给方法 [closed]

Passing null to a method [closed]

提问人:toolkit 提问时间:8/28/2008 更新时间:12/20/2018 访问量:46537

问:

我正在阅读优秀的 Clean Code

一个讨论是关于将 null 值传递到方法中。

public class MetricsCalculator {
    public double xProjection(Point p1, Point p2) {
        return (p2.x - p1.x) * 1.5;
    }
}
...
calculator.xProjection(null, new Point(12,13));

它代表了处理此问题的不同方法:

public double xProjection(Point p1, Point p2) {
    if (p1 == null || p2 == null) {
        throw new IllegalArgumentException("Invalid argument for xProjection");
    }
    return (p2.x - p1.x) * 1.5;
}

public double xProjection(Point p1, Point p2) {
    assert p1 != null : "p1 should not be null";
    assert p2 != null : "p2 should not be null";
    return (p2.x - p1.x) * 1.5;
}

我更喜欢断言方法,但我不喜欢断言默认关闭的事实。

这本书最后说:

在大多数编程语言中,没有很好的方法来处理调用者意外传递的 null。因为是这种情况,所以合理的方法是默认禁止传递 null。

它并没有真正讨论您将如何执行此限制?

无论哪种方式,你们中是否有人有强烈的意见。

java null 断言

评论


答:

1赞 wvdschel 8/28/2008 #1

我通常不喜欢做任何一个,因为这只是减慢了速度。无论如何,稍后都会抛出 NullPointerExceptions,这将很快导致用户发现他们正在将 null 传递给方法。我曾经检查过,但我的 40% 的代码最终都在检查代码,这时我认为它不值得漂亮的断言消息。

3赞 Chris Karcher 8/28/2008 #2

它并没有真正讨论您将如何执行此限制?

如果它们传入 null,则通过抛出 ArgumentExcexception 来强制执行它。

if (p1 == null || p2 == null) {
    throw new IllegalArgumentException("Invalid argument for xProjection");
}
1赞 Lasse V. Karlsen 8/28/2008 #3

我同意或不同意 wvdschel 的帖子,这取决于他具体说什么。

当然,在这种情况下,此方法会崩溃,因此可能不需要此处的显式检查。null

但是,如果该方法只是存储传递的数据,并且稍后调用了一些其他方法来处理它,则尽早发现错误的输入是更快地修复错误的关键。在稍后的一点上,可能会有无数种方式将不良数据恰好提供给您的班级。这有点像试图弄清楚老鼠是如何在事后进入你家的,试图在某个地方找到洞。

2赞 Brian Matthews 8/28/2008 #4

我更喜欢使用断言。

我有一条规则,我只在公共和受保护的方法中使用断言。这是因为我相信调用方法应该确保它将有效的参数传递给私有方法。

评论

0赞 Md. Abu Nafee Ibna Zahid 12/27/2017
docs.oracle.com/javase/7/docs/technotes/guides/language/...不要在公共方法中使用断言进行参数检查。参数检查通常是方法的已发布规范(或协定)的一部分,无论启用还是禁用断言,都必须遵守这些规范。使用断言进行参数检查的另一个问题是,错误的参数应导致适当的运行时异常(IllegalArgumentException、IndexOutOfBoundsException 等)。断言失败不会引发相应的异常。
8赞 aku 8/28/2008 #5

一般规则是,如果方法不需要参数,则应抛出 System.ArgumentNullException。正确抛出不仅可以保护您免受资源损坏和其他不良情况的影响,还可以为代码用户提供指导,从而节省调试代码所花费的时间。nullException

另请阅读一篇关于防御性编程的文章

评论

4赞 John Gardner 10/30/2008
这是一个很好的 c# 答案,不如 java 答案:)
1赞 Shaun Austin 8/28/2008 #6

@Chris Karcher,我会说绝对正确。我唯一要说的是单独检查参数,并让 exption 报告为 null 的参数,因为它可以更轻松地跟踪 null 的来源。

@wvdschel哇!如果编写代码对你来说太费力了,你应该考虑像 PostSharp(或 Java 等效的,如果有的话)这样的东西,它可以对你的程序集进行后处理,并为你插入参数检查。

1赞 Jorge Córdoba 8/28/2008 #7

虽然它并不严格相关,但您可能想看看 Spec#

我认为它仍在开发中(由 Microsoft 开发),但有一些 CTP 可用,看起来很有希望。基本上,它允许您这样做:

  public static int Divide(int x, int y)
    requires y != 0 otherwise ArgumentException; 
  {
  }

  public static int Subtract(int x, int y)
    requires x > y;
    ensures result > y;
  {
    return x - y;
  } 

它还提供了其他功能,如 Notnull 类型。它基于 .NET Framework 2.0 构建,并且完全兼容。如您所见,语法是 C#。

3赞 rcreswick 8/29/2008 #8

Spec# 看起来非常有趣!

当类似的东西不可用时,我通常会使用运行时 null 检查和内部方法的断言来测试非私有方法。我没有在每个方法中显式编写 null 检查代码,而是将其委托给具有 check null 方法的实用程序类:

/**
 * Checks to see if an object is null, and if so 
 * generates an IllegalArgumentException with a fitting message.
 * 
 * @param o The object to check against null.
 * @param name The name of the object, used to format the exception message
 *
 * @throws IllegalArgumentException if o is null.
 */
public static void checkNull(Object o, String name) 
    throws IllegalArgumentException {
   if (null == o)
      throw new IllegalArgumentException(name + " must not be null");
}

public static void checkNull(Object o) throws IllegalArgumentException {
   checkNull(o, "object");
} 

// untested:
public static void checkNull(Object... os) throws IllegalArgumentException {
   for(Object o in os) checkNull(o);  
}

然后检查变成:

public void someFun(String val1, String val2) throws IllegalArgumentException {
   ExceptionUtilities.checkNull(val1, "val1");
   ExceptionUtilities.checkNull(val2, "val2");

   /** alternatively:
   ExceptionUtilities.checkNull(val1, val2);
   **/

   /** ... **/
} 

可以通过编辑器宏或代码处理脚本添加。编辑:详细检查也可以以这种方式添加,但我认为自动添加单行要容易得多。

4赞 Chris Conway 8/29/2008 #9

也不是立即使用,但与提及 Spec#...有人建议在将来的 Java 版本中添加“空安全类型”:“增强的空处理 - 空安全类型”。

根据该提案,您的方法将成为

public class MetricsCalculator {
    public double xProjection(#Point p1, #Point p2) {
        return (p2.x - p1.x) * 1.5;
    }
}

其中 是对 类型的对象的非引用的类型。#PointnullPoint

评论

0赞 Guido 10/30/2008
我真的不知道我是否希望它成为 Java 的一部分,但它非常有趣。
0赞 Cem Catikkas 8/29/2008 #10

在我看来,在方法的开头使用C#或Java是最清晰的解决方案。ArgumentExceptionIllegalArgumentException

应始终小心运行时异常 - 未在方法签名上声明的异常。由于编译器不会强制你捕获它们,因此很容易忘记它们。确保您具有某种“捕获所有”异常处理,以防止软件突然停止。这是用户体验中最重要的部分。

0赞 Scott Dorman 8/29/2008 #11

处理此问题的最好方法是使用异常。最终,断言最终会为最终用户提供类似的体验,但在向最终用户显示异常之前,无法为调用代码的开发人员提供处理这种情况的方法。最后,您希望确保尽早测试无效输入(尤其是在面向公众的代码中),并提供调用代码可以捕获的适当异常。

3赞 Andrei 8/29/2008 #12

在大多数编程语言中,没有很好的方法来处理调用者意外传递的 null。因为是这种情况,所以合理的方法是默认禁止传递 null。

到目前为止,我发现 JetBrains 和注释处理这个问题的方法最巧妙。不幸的是,它是特定于 IDE 的,但非常干净和强大,IMO。@Nullable@NotNull

http://www.jetbrains.com/idea/documentation/howto.html

将这个(或类似的东西)作为 java 标准会非常好。

4赞 Russell Mayor 8/29/2008 #13

在这里,使用断言和抛出异常都是有效的方法。这两种机制都可用于指示编程错误,而不是运行时错误,就像这里的情况一样。

  • 断言具有性能优势,因为它们通常在生产系统上被禁用。
  • 例外具有安全优势,因为始终执行检查。

选择实际上取决于项目的开发实践。整个项目需要决定一个断言策略:如果选择在所有开发过程中启用断言,那么我会说使用断言来检查这种无效参数 - 在生产系统中,由于编程错误而抛出的 NullPointerException 无论如何都不太可能以有意义的方式捕获和处理,因此会像断言一样行事。

但实际上,我知道很多开发人员不相信断言会在适当的时候启用,因此选择抛出 NullPointerException 的安全性。

当然,如果您无法为代码强制执行策略(例如,如果您正在创建库,因此依赖于其他开发人员运行代码的方式),则应选择为库 API 中的方法抛出 NullPointerException 的安全方法。

1赞 Russell Mayor 8/29/2008 #14

有点跑题了,但我认为 findbugs 的一个非常有用的功能是能够注释方法的参数以描述哪些参数不应该传递 null 值。

通过对代码进行静态分析,findbugs 可以指出调用方法的位置,该方法可能为 null 值。

这有两个优点:

  1. 该批注描述了您应如何调用该方法的意图,有助于文档编制
  2. FindBugs 可以指向该方法的潜在问题调用方,从而可以跟踪潜在的 bug。

仅当您有权访问调用方法的代码时才有用,但通常情况如此。

0赞 Damien B 8/29/2008 #15

以 Java 的方式,假设 null 来自编程错误(即永远不应该超出测试阶段),然后让系统抛出它,或者如果有副作用达到这一点,请在开始时检查 null 并抛出 IllegalArgumentException 或 NullPointerException。

如果 null 可能来自实际的异常情况,但您不想为此使用已检查的异常,那么您肯定希望在方法开头使用 IllegalArgumentException 路由。

1赞 Marcus Downing 10/30/2008 #16

由于偏离主题似乎已成为话题,Scala 对此采取了一种有趣的方法。假定所有类型都不是 null,除非您将其显式包装在 an 中以指示它可能是 null。所以:Option

//  allocate null
var name : Option[String]
name = None

//  allocate a value
name = Any["Hello"]

//  print the value if we can
name match {
  Any[x] => print x
  _ => print "Nothing at all"
}

评论

0赞 toolkit 10/30/2008
有趣的:-)将需要阅读 Scala。