将Null对象拆箱为基本类型会导致NullPointerException,可以吗?

61

这段代码由于拆箱为基本类型并调用了Long.longValue(),会抛出NullPointerException异常,对吧?

如果你有类似下面的代码片段,很容易看出来:

long value = (Long) null;

但是在更复杂的情况下,像这样,NullPointerException 更难被获得:

long propertyValue = (Long) obj.getProperty(propertyModel.getName());

那么Java编译器有没有可能将其转换为更舒适的异常呢?我更喜欢一个带有消息“您正在尝试将null对象转换为原始类型,这是不可能的!”的IllegalArgumentException

这样会更合适吗?你认为这可能吗?在运行时我们能确定这个转换吗?我还没有查看Java字节码。也许它可以用于解决方案。

这个问题可以回答:我想知道是否有可能实现这种行为!


1
NPE对我来说没问题。可能会有助于使用空值注解。(顺便提一下,拆箱和装箱。) - Tom Hawtin - tackline
4个回答

104
根据Java编程语言规范,拆箱是通过调用Number.longValue()Number.intValue()等方法实现的。没有特殊的字节码魔术,这与手动调用这些方法完全相同。因此,将null值进行拆箱操作会自然地导致NullPointerException异常(事实上,这是由JLS规定的)。
抛出其他异常需要在每次拆箱转换时检查null值两次(一次确定是否抛出特殊异常,一次在实际调用方法时隐式检查)。我想语言设计师认为这并不值得实现。

8
当对一个空对象调用longValue()或doubleValue()这样的方法时,会出现NullPointerException的错误。解释为什么会出现这个错误是非常有帮助的。在类似的问题中,很多答案都只是简单地回答“因为自动拆箱会抛出NullPointerException”。 - kiedysktos

25

自Java 8 SE起,也引入了Optional.ofNullable函数。

long value = Optional.ofNullable(obj.getProperty(propertyModel.getName())).orElse(0L)));

1

对于这种情况,编写一个小的私有助手是个好主意。这些助手可以处理正确转换、错误消息和默认值。

在异常中放入足够的操作“状态”是很好的做法(在这种情况下,选项名称和值,甚至选项映射的字符串表示形式如果未找到也可以)。

例如:

private long safeGetLong(Map<String, Option> options, String name) {
  if (name == null || options == null)
    throw new IllegalArgumentExcption("You need to give options and name. (name="+name+", opts=" + options));
  Object val = options.get(name);
  if (val == null)
    throw new ConfigurationException("The option name="+name+" is unknown");
  if (val instanceof Long)
    return val.longValue();

  String strVal = null;
  try
  {
    strVal = val.toString();
    return Long.parseValue(strVal);
  } catch (Exception ex) {
    throw new ConfigurationException("Cannot parse " + name + "=" + strVal + " into a Long.");
  }
}

当然,拥有一个允许类型访问的配置对象会更好。
有一些验证框架可以为您完成此操作,但我通常会自己编写代码,因为它更适合应用程序中的IN8L和异常层次结构或日志记录约定。很难使其通用化。

1

这并不是 IllegalArgumentException 的意思。编译器无法保证值在运行时是否为 null。它所知道的只有类型,在您的示例中很可能是 String

当异常被抛出时,编译器确实知道问题是一个 null 值。如果您正在使用调试器,您可以自己看到这一点。因此,从技术角度来看 - 这是对您的问题的简短回答 - 是的,有可能创建一个编译器,将其包含在错误描述中。但是,如果您想要针对 null 值的特殊消息,下一步是什么?对于超出某些可接受范围10个单位的整数,需要特殊消息吗?尽管这有点愚蠢,但我希望它能说明问题。


我的示例中返回类型是Object,如果是String,我可以使用Long.valueOf()。它不一定是IllegalArgumentException。我想要一个特殊的异常,因为对于不了解装箱/拆箱的人来说,这里有一些魔法。但好吧,这是你的意见 :) - Christopher Klewes
我不明白你所说的强制和魔法是什么意思,但如果你有更具体的问题,请发布额外的示例代码。变量类型之类的信息将特别有用。 - Pops

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接