Java 将Null对象取消绑定到基元类型会导致NullPointerException,好吗?

Java 将Null对象取消绑定到基元类型会导致NullPointerException,好吗?,java,nullpointerexception,Java,Nullpointerexception,这个代码段抛出了一个NullPointerException,因为它未绑定到一个基元类型,并且调用了Long.longValue(),对吗 如果您有这样一个片段,那么很容易看到: long value = (Long) null; long propertyValue = (Long) obj.getProperty(propertyModel.getName()); 但是NullPointerException在更复杂的情况下更难获得: long value = (Long) null;

这个代码段抛出了一个
NullPointerException
,因为它未绑定到一个基元类型,并且调用了
Long.longValue()
,对吗

如果您有这样一个片段,那么很容易看到:

long value = (Long) null;
long propertyValue = (Long) obj.getProperty(propertyModel.getName());
但是
NullPointerException
在更复杂的情况下更难获得:

long value = (Long) null;
long propertyValue = (Long) obj.getProperty(propertyModel.getName());
那么,Java编译器是否有可能对此进行更舒适的异常处理呢?我更喜欢一个带有如下消息的
IllegalArgumentException
“您试图将空对象强制转换为基元类型,这是不可能的!”

这不是更合适吗?你怎么认为?这在运行时是否可能?我们能确定这个演员阵容吗?我还没有看过java字节码。也许它可以用于解决问题


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

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

当然,在运行时抛出异常时,编译器知道问题是
null
值。如果您使用的是调试器,您可以自己看到这一点。因此,从技术角度来看——这是对您问题的简短回答——是的,可以创建一个编译器,将其包含在错误描述中。但是如果您想要一条关于
null
值的特殊消息,下一步是什么?对于超出某个可接受范围超过10的整数的特殊消息?诚然,这是一个愚蠢的例子,但我希望它是说明性的。

根据,通过调用
Number.longValue()
Number.intValue()
,等来进行取消装箱。没有特殊的字节码魔法发生,这与手动调用这些方法完全相同。因此,
NullPointerException
是取消绑定
null
的自然结果(事实上是由JLS强制执行)


抛出不同的异常需要在每次取消装箱转换期间检查两次
null
(一次确定是否抛出特殊异常,一次在实际调用该方法时隐式地进行)。我想语言设计者认为它没有足够的用处来保证这一点。

为这种情况编写一个小型私人助手是个好主意。这些可以处理生成正确的强制转换、错误消息和默认值

将足够多的操作“状态”放入异常(在本例中是选项名和值,如果找不到,甚至可能是选项映射的字符串表示形式)是很好的

比如:

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.");
  }
}
private long safeGetLong(映射选项、字符串名称){
if(name==null | | options==null)
抛出新的IllegalArgumentException(“您需要提供选项和名称。(name=“+name+”,opts=“+options”);
Object val=options.get(name);
if(val==null)
抛出新的ConfigurationException(“选项名称=“+name+”未知”);
if(val instanceof Long)
返回值longValue();
字符串strVal=null;
尝试
{
strVal=val.toString();
返回Long.parseValue(strVal);
}捕获(例外情况除外){
抛出新的ConfigurationException(“无法将“+name+”=“+strVal+”解析为长字符串”);
}
}
当然,拥有允许类型化访问的配置对象更好


有一些验证框架可以为您做到这一点,但我通常会自己编写代码,因为它更适合8L和异常层次结构或相关应用程序的日志约定。很难做到通用化。

因为Java8SE还有可选的。ofNullable

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

NPE对我来说很好。Nullability注释可能会有所帮助。(取消装箱,顺便说一句)在我的示例中,返回类型是Object,它是字符串吗?我可以使用Long.valueOf()。它不是被迫成为非法辩论的例外。我想有一个特别的例外,因为有人做了一些魔术,人们不知道拳击/拆箱。但好吧,这是你的观点:)我不明白你说的强制和魔法是什么意思,但如果你有更具体的问题,请发布额外的示例代码。变量类型之类的信息尤其有用。解释为什么会发生这种情况有一个很大的好处——对空对象调用longValue()或doubleValue()之类的方法。类似线程中的许多答案最后都会说“因为取消装箱会引发NullPointerException”