我目前正在为AP Java中的一名高中生辅导,她向我问了一个关于" double casting"的问题。我以前从未听过这个词,但显然她的老师希望她知道即将到来的决赛。
她的老师提供的示例是,如果您想将Integer转换为String,则必须执行以下操作以避免编译错误:
Integer i = new Integer(5);
String s = (String)(Object) i;
问题是:你想在现实生活中什么时候做这件事?
教师仅提供导致运行时错误的示例。此外,我从来没有真正知道有这个术语,但这似乎是一个坏主意,因为当两种类型不兼容时,只有一个错误。
谢谢!
答案 0 :(得分:7)
我的意思是,有自动toString
来电,例如println("" + i)
,但即使这样,你也不需要先投射到一个物体......
编辑:在阅读了Tom的回答后,我突然不确定这个答案 - 原语和(特别是)泛型实际上可以使用它。我现在没有能力测试任何东西,但是任何读过这个答案的人都应该肯定看一下他的(并且可能是赞成它)。
我会坚持认为没有(或者至少极少数和很少)良好原因的行,但是,提供的示例当然无关用它。
答案 1 :(得分:7)
虽然“双重演员”当然不是一个常用的术语,你不应该看到任何类型的参考文献,你应该知道会发生什么(ClassCastException
)。
为了完整起见,有些情况下不会有CCE:
null
。Object
到Integer
到[拆箱] int
,或int
到[有损] byte
到[正] char
)< / LI>
List<String>
至Object
至List<Integer>
。答案 2 :(得分:1)
在大多数情况下,双重投射确实会导致ClassCastException
,但有一些特殊情况。正如其他答案中所提到的,它可以用于处理原语和泛型,但是当编译的类不必代表运行时类时也可以使用它。当涉及ASM字节码类变换器时尤其如此。
举个例子,我将使用Mixin框架。以下代码将在运行时注入Foo
类。我不会深入了解这是如何工作的具体细节。
@Mixin(Foo.class)
public class MixinFoo {
public Foo bar() {
return (Foo) (Object) this;
}
}
this
的编译时类型似乎是MixinFoo
,但实际上这个方法是动态插入的,因此在运行时,它将是Foo
。编译器不知道这一点;我们必须将其强制转换为Foo
,但这也会导致编译错误。
首先转换为Object
然后转换为Foo
(双重转换)可以解决编译器的问题。请记住,如果该方法未在运行时应用,则会导致运行时错误。
当然,这远远超出了Java介绍的技能水平。
答案 3 :(得分:0)
当然,虽然这只是避免编译器错误,但必然会发生运行时错误。此外,如果两个类在继承层次结构中,则无需向下转换为基类并再次向上转发。
即使在上述问题中,最终也需要一种API方法来转换对象。
答案 4 :(得分:0)
我最近遇到过这个问题并且使用它。我有一个包含2个整数值和一个字符串的对象数组。当我来解压缩它时,我需要将整数转换为double以进行一些除法。
问题在于,当我来转换整数值以使其错误加倍时,因为Java会自动将其检测为Integer
对象而不是int
原语,并且您无法从Integer转换为double
因此,解决方案是在进行划分之前使用(double)(int)value
。
总而言之,如果您将int存储为对象并且想要将其转换为某个除法的double,则java会自动将其转换为Integer,从而无法进行除法。
答案 5 :(得分: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
}
答案 6 :(得分: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。
答案 7 :(得分: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
将是一个有效的类型标记,并且您不需要任何类型转换,更不用说奇怪的双重原始到假泛型转换了。
答案 8 :(得分:-1)
也许这个问题是逻辑?我的意思是,有一些方法可以计算某些东西(使用整数)并返回Object,并使用方法将结果转换为String,通常这个metod可以被覆盖几次,这样的功能在Java 1.5之前使用你定义了Generic calss(但没有Generics)并且每个方法的返回都是Object因为有几个孩子并且每个都可以返回自己的类型,我不知道但是可以回答,并且像这样双重投射
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
}
}
答案 9 :(得分:-1)
虽然双重演员在程序员的前瞻性中可能看起来更加重要,但至少知道语法的工作方式可能有助于您理解Java的基本工作原理。
例如:
假设Animal
是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
我们将d
转换为Animal
对象,但编译器隐式将其作为Object
对象进行转换。双重铸造。
哪一个d
最终被投放为?
明确地说,它是Object a = (Object)(Animal)d;
了解双重拼接的语法,我们知道d
最终会被归为Object
,因此不会发生错误。