将 null 传递给方法

发布于 2024-07-04 15:47:22 字数 1101 浏览 10 评论 0原文

我正在阅读优秀的干净的代码

其中一个讨论是关于传递空值进入一个方法。

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;
}

我更喜欢 断言< /a> 方法,但我不喜欢默认情况下关闭断言的事实。

书中最后指出:

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

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

不管怎样,你们中有人有强烈的意见吗?

I am in the middle of reading the excellent Clean Code

One discussion is regarding passing nulls into a method.

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

It represents different ways of handling this:

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;
}

I prefer the assertions approach, but I don't like the fact that assertions are turned off by default.

The book finally states:

In most programming languages there is no good way to deal with a null that is passed by a caller accidentally. Because this is the case, the rational approach is to forbid passing null by default.

It doesn't really go into how you would enforce this restriction?

Do any of you have strong opinions either way.

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

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

发布评论

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

评论(16

把人绕傻吧 2024-07-11 15:47:23

我更喜欢使用断言。

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

I prefer the use of assertions.

I have a rule that I only use assertions in public and protected methods. This is because I believe the calling method should ensure that it is passing valid arguments to private methods.

不离久伴 2024-07-11 15:47:23

尽管它并不严格相关,但您可能需要查看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#。

Although it is not strictly related you might want to take a look to Spec#.

I think it is still in development (by Microsoft) but some CTP are available and it looks promising. Basically it allows you to do this:

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

or

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

It also provides another features like Notnull types. It's build on top of the .NET Framework 2.0 and it's fully compatible. The syntaxt, as you may see, is C#.

甜味拾荒者 2024-07-11 15:47:23

@Chris Karcher 我想说绝对正确。 我唯一要说的是单独检查参数,并让异常报告为空的参数,因为它使得跟踪空值的来源变得更加容易。

@wvdschel 哇! 如果编写代码对您来说太费力,您应该研究类似 PostSharp (或 Java 等效项)如果有的话)它可以对您的程序集进行后处理并为您插入参数检查。

@Chris Karcher I would say absolutely correct. The only thing I would say is check the params separately and have the exeption report the param that was null also as it makes tracking where the null is coming from much easier.

@wvdschel wow! If writing the code is too much effort for you, you should look into something like PostSharp (or a Java equivalent if one is available) which can post-process your assemblies and insert param checks for you.

快乐很简单 2024-07-11 15:47:23

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

//  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"
}

Since off-topic seems to have become the topic, Scala takes an interesting approach to this. All types are assumed to be not null, unless you explicity wrap it in an Option to indicate that it might be null. So:

//  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"
}
反话 2024-07-11 15:47:23

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

I generally prefer not doing either, since it's just slowing things down. NullPointerExceptions are thrown later on anyway, which will quickly lead the user to discovering they're passing null to the method. I used to check, but 40% of my code ended up being checking code, at which point I decided it was just not worth the nice assertion messages.

明明#如月 2024-07-11 15:47:23

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

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

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

I agree or disagree with wvdschel's post, it depends on what he's specifically saying.

In this case, sure, this method will crash on null so the explicit check here is probably not needed.

However, if the method simply stores the passed data, and there is some other method that you call later that will deal with it, discovering bad input as early as possible is the key to fixing bugs faster. At that later point, there could be a myriad of ways that bad data happened to be given to your class. It's sort of trying to figure out how the rats came into your house after the fact, trying to find the hole somewhere.

洒一地阳光 2024-07-11 15:47:23

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

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

In a Java way, assuming the null comes from a programming error (ie. should never go outside the testing phase), then leave the system throw it, or if there are side-effects reaching that point, check for null at the beginning and throw either IllegalArgumentException or NullPointerException.

If the null could come from an actual exceptional case but you don't want to use a checked exception for that, then you definitely want to go the IllegalArgumentException route at the beginning of the method.

白龙吟 2024-07-11 15:47:23

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

我发现 JetBrains 的 @Nullable@NotNull 注释方法处理这个问题是迄今为止最巧妙的。 不幸的是,它是特定于 IDE 的,但在我看来确实干净且强大。

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

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

In most programming languages there is no good way to deal with a null that is passed by a caller accidentally. Because this is the case, the rational approach is to forbid passing null by default.

I found JetBrains' @Nullable and @NotNull annotations approach for dealing with this the most ingenious, so far. It's IDE specific, unfortunately, but really clean and powerful, IMO.

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

Having this (or something similar) as a java standard would be really nice.

傲娇萝莉攻 2024-07-11 15:47:23

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

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

if (p1 == null || p2 == null) {
    throw new IllegalArgumentException("Invalid argument for xProjection");
}

It doesn't really go into how you would enforce this restriction?

You enforce it by throwing an ArgumentExcexception if they pass in null.

if (p1 == null || p2 == null) {
    throw new IllegalArgumentException("Invalid argument for xProjection");
}
机场等船 2024-07-11 15:47:23

在我看来,在方法的开头抛出 C# ArgumentException 或 Java IllegalArgumentException 是最清晰的解决方案。

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

Thwrowing C# ArgumentException, or Java IllegalArgumentException right at the beginning of the method looks to me as the clearest of solutions.

One should always be careful with Runtime Exceptions - exceptions that are not declared on the method signature. Since the compiler doesn't enforce you to catch these it's really easy to forget about them. Make sure you have some kind of a "catch all" exception handling to prevent the software to halt abruptly. That's the most important part of your user experience.

岁月打碎记忆 2024-07-11 15:47:23

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

The best way to handle this really would be the use of exceptions. Ultimately, the asserts are going to end up giving a similar experience to the end user but provide no way for the developer calling your code to handle the situation before showing an exception to the end user. Ultimatley, you want to ensure that you test for invalid inputs as early as possible (especially in public facing code) and provide the appropriate exceptions that the calling code can catch.

獨角戲 2024-07-11 15:47:23

有点偏离主题,但我认为 findbugs 的一个功能非常有用,就是能够注释方法的参数来描述哪些参数不应传递空值。

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

这有两个优点:

  1. 注释描述了您对如何调用该方法的意图,辅助文档
  2. FindBugs 可以指出该方法的潜在问题调用者,使您能够追踪潜在的错误。

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

Slightly off-topic, but one feature of findbugs that I think is very useful is to be able to annotate the parameters of methods to describe which parameters should not be passed a null value.

Using static analysis of your code, findbugs can then point out locations where the method is called with a potentially null value.

This has two advantages:

  1. The annotation describes your intention for how the method should be called, aiding documentation
  2. FindBugs can point to potential problem callers of the method, allowing you to track down potential bugs.

Only useful when you have access to the code that calls your methods, but that is usually the case.

夜未央樱花落 2024-07-11 15:47:23

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

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

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

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

Also not of immediate use, but related to the mention of Spec#... There's a proposal to add "null-safe types" to a future version of Java: "Enhanced null handling - Null-safe types".

Under the proposal, your method would become

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

where #Point is the type of non-null references to objects of type Point.

半山落雨半山空 2024-07-11 15:47:23

Spec# 看起来很有趣!

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

/**
 * 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);
   **/

   /** ... **/
} 

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

Spec# looks very interesting!

When something like that isn't available, I generally test non-private methods with a run-time null-check, and assertions for internal methods. Rather than code the null check explicitly in each method, I delegate that to a utilities class with a check null method:

/**
 * 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);  
}

Then checking turns into:

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

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

   /** ... **/
} 

That can be added with editor macros, or a code-processing script.
Edit: The verbose check could be added this way as well, but I think it's significantly easier to automate the addition of a single line.

小梨窩很甜 2024-07-11 15:47:23

使用断言和抛出异常都是有效的方法。 任一机制都可用于指示编程错误,而不是运行时错误,如此处的情况。

  • 断言具有性能优势,因为它们通常在生产系统上被禁用。
  • 异常具有安全性的优点,因为总是执行检查。

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

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

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

Both the use of assertions and the throwing of exceptions are valid approaches here. Either mechanism can be used to indicate a programming error, not a runtime error, as is the case here.

  • Assertions have the advantage of performance as they are typically disabled on production systems.
  • Exceptions have the advantage of safety, as the check is always performed.

The choice really depends on the development practices of the project. The project as a whole needs to decide on an assertion policy: if the choice is to enable assertions during all development, then I'd say to use assertions to check this kind of invalid parameter - in a production system, a NullPointerException thrown due to a programming error is unlikely to be able to be caught and handled in a meaningful way anyway and so will act just like an assertion.

Practically though, I know a lot of developers that don't trust that assertions will be enabled when appropriate and so opt for the safety of throwing a NullPointerException.

Of course if you can't enforce a policy for your code (if you're creating a library, for example, and so are dependent on how other developers run your code), you should opt for the safe approach of throwing NullPointerException for those methods that are part of the library's API.

小嗲 2024-07-11 15:47:22

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

另请阅读有关防御性编程的文章

General rule is if your method doesn't expect null arguments then you should throw System.ArgumentNullException. Throwing proper Exception not only protects you from resource corruption and other bad things but serves as a guide for users of your code saving time spent debugging your code.

Also read an article on Defensive programming

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