Java无法将封装类型转换为原始类型。

3
我正在尝试使用Class.cast()将包装的Double转换为原始double类型:
Object d = 1.0d;
Class<?> clazz = double.class;
//the bellow line throws java.lang.ClassCastException: 
//Cannot cast java.lang.Double to double
clazz.cast(d);

我正在做这件事情,是因为在我的代码的某个部分中,动态给定了一个值对象和一个类,并且我确信该值对象与兼容类型相同。但是这个异常真的让我感到困惑。使用cast()的契约是什么? 更新:我遇到这个问题是因为我们有这样的代码:
interface Provider <T> {
  T get ();
  Class<T> getClazz();
}

//a special handling for double primitive
class DoubleProvider implements Provider<Double> {

  public double getDouble(){
      return 1.0d;
  }

  @Override
  public Double get() {
    return getDouble();
  }

  @Override
  public Class<Double> getClazz() {
    //why this actually compiles?
    return double.class;
  }
}

如果double和Double是如此不同,以至于它们不能被视为可分配的任何一种方式,那么为什么Java允许在DoubleProvider中编译getClazz()方法?double.class和Double.class之间的关系是什么?

1
你不能以那种方式将包装类转换为原始类型。自动装箱和强制类型转换是不同的东西。 - azurefrog
2个回答

7
您不能将Double转换为double(反之亦然),因为两者都不是对方的子类型。请使用(double)自动取消装箱值。

使用cast()的契约是什么?

API的javadoc是该方法的契约。在这种情况下,javadoc说:

将对象强制转换为此Class对象表示的类或接口。

[...]

抛出:如果对象不为空且无法分配给类型T,则为ClassCastException。

如果需要基于类获取对象的原始(取消装箱)版本,则我认为没有比以下更好的方法了

if (clazz == double.class)
    handleDouble((double) o);
if (clazz == int.class)
    handleInt((int) o);
...

关于您的编辑:这是因为double.class的类型是Class<Double>JLS声明

在其中p是原始类型(§4.2)的名称时,p.class的类型是Class<B>,其中B是类型为p的表达式经过装箱转换(§5.1.7)之后的类型。


JLS语句有意义,但Double.class.equals(double.class)返回false? - Xinchao
是的,你说得对。我在回答时太快了。重新措辞了。谢谢。 - aioobe

4

自动装箱和拆箱本质上是编译器的一个技巧。它的主要目的是掩盖基本类型不能赋值或转换为其对应的包装类类型,反之亦然的事实。每当您认为将double赋值给Double类型的变量(例如)时,实际上您首先会将double进行自动装箱成Double,然后再进行赋值,就好像您编写了以下内容:

Double wrapper = Double.valueOf(1.0d);

过去,我们必须手动完成这项工作。

我这样做是因为在我的代码的某个部分中,动态给出了一个值对象和一个类

如果您确实收到一个值对象,那么问题不应该出现。原始类型不是对象,Java没有提供处理真正的原始类型的方法,使您可以将其与对象混淆。如果您有一个接受Object引用的方法,或者如果您有一个变量来保存它,则可以100%确信所引用的对象不是double或任何其他原始类型,但它可能是Double或其他包装类的实例。

更新: 如果您的系统需要处理原始类型和包装类型的值,并保持它们之间的区别,则可能需要针对原始类型提供特殊情况。您可以通过Class.isPrimitive()识别表示原始类型的类。其中恰好有九个,包括Void.TYPE

更新2: 回答新增问题时,Double.TYPE的类型(原始且仍然有效的拼写方式为double.class)为Class<Double>。因此,它是具有该返回类型的方法的有效返回值。 “为什么?”问题总是有点棘手,但我认为可以推断出至少一个原因,即Double.TYPE具有特定类参数的原因之一是没有更好的替代方案。类型参数必须是引用类型的名称。 double不是引用类型,因此double.class的类型不能为Class<double>Class<Double>是次佳选择。

请注意:尽管Double.classdouble.class都具有类型java.lang.Class<java.lang.Double>,但它们是不同的类。


好的。实际上我的情况是相反的:我得到了一个Double值对象和double.class类对象。因此,我正在尝试double.class.cast(Double),这本质上是同样的问题。 - Xinchao
@Xinchao,我已经更新了我的答案,并为您的情况提供了一般性建议。 - John Bollinger
谢谢!我也更新了我的问题,包括我的另一个困惑 :) - Xinchao
@Xinchao,我已经再次更新了我的答案。但是,如果您有后续问题,则正确的SO协议是将它们发布为单独的问题。 - John Bollinger

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