14

我目前正在辅导一名 AP Java 高中生,她问我一个关于“双重铸造”的问题。我以前从未听说过这个学期,但显然她的老师希望她在即将到来的期末考试中知道它。

她的老师提供的示例是,如果您想将 Integer 转换为 String,则必须执行以下操作才能避免编译器错误:

Integer i = new Integer(5);
String s = (String)(Object) i;

问题是:你什么时候想在现实生活中这样做?

老师只提供了导致运行时错误的示例。另外,我从来不知道有这个术语,但这样做似乎是个坏主意,因为当这两种类型不兼容时只会出现错误。

谢谢!

4

10 回答 10

8

虽然“双重转换”当然不是一个常用术语,而且您似乎也不应该对引用进行任何类型的转换,但您应该知道会发生什么(a ClassCastException)。

为了完整起见,在某些情况下它不会 CCE:

  • 如果值实际上是null.
  • 涉及原语的东西。(呃,Objectto Integerto [unboxing] int,或者intto [lossy] byteto [positive] char
  • 更改泛型类型参数。List<String>ObjectList<Integer>
于 2013-01-21T03:19:55.653 回答
7

是的,我很确定这不是一回事。没有理由需要双重转换 - 它可能会消除有关不安全转换的编译警告(在这种情况下,您可能做错了),但否则这是不对的。

我的意思是,有自动toString调用 eg println("" + i),但即使那样你也不需要先转换为对象......

编辑:阅读汤姆的回答后,我突然不确定这个答案 - 原语和(特别是)泛型实际上可以使用它。我现在没有能力测试任何东西,但是任何阅读这个答案的人都应该看看他的(并且可能会赞成)。

但是,我将坚持没有(或至少极少且相差甚远)充分的理由这样做,并且提供的示例肯定与此无关。

于 2013-01-21T02:56:45.110 回答
3

确实,在大多数情况下,双重转换会导致 a ClassCastException,但也有一些特殊情况。正如其他答案中提到的,它对于处理原语和泛型很有用,但是当编译的类不一定代表运行时类时也可以使用它。当涉及 ASM 字节码类转换器时尤其如此。

例如,我将使用Mixin框架。以下代码将Foo在运行时注入到类中。我不会详细说明它是如何工作的。

@Mixin(Foo.class)
public class MixinFoo {

    public Foo bar() {
        return (Foo) (Object) this;
    }
}

的编译时类型this似乎是MixinFoo,但这个方法实际上是动态插入的,所以在运行时,它会是Foo. 编译器不知道这一点;我们必须将其强制转换为Foo,但这也会导致编译器错误。

先转换ObjectFoo(双重转换)将解决编译器的问题。请记住,如果该方法未在运行时应用,这将导致运行时错误。

当然,这远远超出了 Java 入门的技能水平。

于 2018-04-10T01:16:30.190 回答
0

当然,虽然这只是避免了编译器错误,但运行时错误肯定会发生。此外,如果两个类在继承层次结构中,则无需向下转换为基类并再次向上转换。

即使在上面的问题中,最终也需要一个API方法来转换对象。

于 2013-01-21T02:56:41.473 回答
0

我最近遇到了这个问题并使用它。我有一个包含 2 个整数值和一个字符串的对象数组。当我来解压它时,我需要将整数转换为双精度以进行一些除法。

问题是,当我将整数值转换为加倍时会出错,因为 Java 会自动将其检测为Integer对象而不是int原始对象,并且您无法从整数转换为加倍。

因此,解决方案是(double)(int)value在进行除法之前使用。

所以总结一下,如果你将 int 存储为对象,并且你想将它转换为 double 以进行某些除法,java 会自动将它转换为 Integer,从而无法进行除法。

于 2016-04-05T20:04:21.443 回答
0

一个真正的用例是:

static class Y extends X<Date> {
//some code
}

List<Y> n = new ArrayList<>();

someMethod((List<X<Date>>)(List<?>) n);

在哪里

void someMethod(List<X<Date>>){
//some code
}
于 2018-02-16T14:29:43.523 回答
0

这是我遇到的一个简单的现实世界示例,您需要帮助编译器理解它应该在强制转换之前自动装箱:

class NumberConverter<F extends Number, T> {

    public T convertNumber(F value) {
        if (targetType.equals(Integer.class) || targetType.equals(int.class)) {
            return (T) (Object) value.intValue();
        }

        (..)
    }
}

您不能直接转换为 T,但可以转换为 Object,这会导致 JVM 在将其转换为 Object 之前对其进行自动装箱。然后你可以毫无问题地转换为 T 。

于 2018-09-08T08:30:52.100 回答
0

是的,当您需要处理已擦除泛型和类型标记的交集时。

假设您想要一个在编译时和运行时都是类型安全的通用函数。所以你用类型标记声明它:

public static <T> T getT(
    ExampleDataSource exampleDataSource,
    String key,
    Class<T> typeToken) {

    // code goes here...
}

只要您只尝试从数据源中检索非泛型类型,这将非常有效。例如:

String string = getT(source, "some-string", String.class);
int integer = getT(source, "some-integer", Integer.class);
DateTime dateTime = getT(source, "some-datetime", DateTime.class);

但是,如果您想List<String>从数据源中获取信息,会发生什么?好吧,它的语法是一个非常丑陋的双重转换。它还要求数据源能够自己找出擦除的类型,因为它在运行时不存在于类型标记中,尽管进行了强制转换:

List<String> listOfString = getT(
    source,
    "some-list-of-strings",
    (Class<List<String>>) (Class) List.class
);

为什么 Java 不允许一步List.class直接转换为(虚构的)类型Class<List<String>>,我无法告诉你。我希望 Java 在未来的某个时候能够具体化泛型类型。然后List<String>.class将是一个有效的类型标记,并且您不需要任何转换,更不用说奇怪的双重原始到假通用转换了。

于 2021-01-29T16:54:54.817 回答
-1

也许这个问题是逻辑?我的意思是,有一种方法可以计算一些东西(使用整数)并返回对象,以及将结果转换为字符串的方法的使用,通常这个方法可以被覆盖几次,像这样的功能在 Java 1.5 之前使用过你是否定义了 Generic calss(但没有泛型)并且每个方法的返回都是对象,因为有几个孩子,每个都可以返回自己的类型,我不知道但可以回答,像这样的双重转换

public class Main {
    public static void main(String[] args) {

        AbstractToDO abstractToDO2 = new SomeToDoTwo();
        String result2 = (String) abstractToDO2.toDo(); // here is second, all is good

        System.out.println(result2);

        AbstractToDO abstractToDO1 = new SomeToDoOne();
        String result1 = (String) abstractToDO1.toDo(); // here is second, Runtime error

        System.out.println(result1);

    }

    Object onePlusOne(){

        return 1+1 +" ";
    }
}

interface AbstractToDO{
     Object toDo();
}

class SomeToDoOne implements AbstractToDO{
    @Override
    public Object toDo() {
        return (Object)(1+1); // here is first casting, // we can use without (Object) casting
    }
}class SomeToDoTwo implements AbstractToDO{
    @Override
    public Object toDo() {
        return (Object)"1+1"; // here is first casting, // we can use without (Object) casting
    }
}
于 2013-01-21T03:25:37.943 回答
-1

尽管双重转换在程序员看来似乎是多余的,但至少了解语法的工作原理可以帮助您理解 Java 的底层工作原理。

例如:

假设 anAnimal是一个类的超Dog类。我们知道Object类是所有类的超类。

Dog d = new Dog(); 
Object a = d;          //no errors occur

在这里,d被隐式转换为Object对象。明确地说,这将是Object a = (Object)d;

这里怎么样?

Dog d = new Dog(); 
Object a = (Animal)d;  //no errors occur

我们将其转换dAnimal对象,但编译器将其隐式转换为Object对象。双铸。

d最终被选为哪一个?

明确地说,这将是Object a = (Object)(Animal)d;

知道双重转换的语法,我们会知道d最终会被转换为Object,因此不会发生错误。

于 2017-06-22T18:42:08.833 回答