[英]“Double casting”
我目前正在辅导一名高中生的 AP Java,她问了我一个关于“双重铸造”的问题。 我以前从未听说过这个术语,但显然她的老师希望她在即将到来的决赛中知道它。
她的老师提供的示例是,如果您想将 Integer 转换为 String,则必须执行以下操作以免出现编译器错误:
Integer i = new Integer(5);
String s = (String)(Object) i;
问题是:你想在现实生活中什么时候这样做?
老师只提供了导致运行时错误的示例。 此外,我从来不知道有这样的术语,但这样做似乎是个坏主意,因为只有当这两种类型不兼容时才会出现错误。
谢谢!
是的,我很确定那不是一回事。 没有理由需要双重转换——它可能会消除关于不安全转换的编译警告(在这种情况下你可能做错了),但否则就是不对的。
我的意思是,有自动toString
调用,例如println("" + i)
,但即便如此,您也不需要先转换为对象...
编辑:阅读汤姆的回答后,我突然不确定这个答案 - 原语和(特别是)泛型实际上可以使用它。 我没有能力来测试什么的权利,但任何人阅读这个答案应该绝对看看他(可能给予好评吧)。
但是,我将坚持认为没有(或至少极少且相距甚远)充分理由这样做,并且提供的示例肯定与此无关。
虽然“双重转换”当然不是一个常用术语,而且您看起来也不应该有任何类型的引用转换,但您应该知道会发生什么( ClassCastException
)。
为了完整起见,在某些情况下它不会 CCE:
null
。Object
to Integer
to [unboxing] int
,或int
to [lossy] byte
to [positive] char
)List<String>
到Object
到List<Integer>
。确实,双重转换在大多数情况下会导致ClassCastException
,但有一些特殊情况。 正如其他答案中提到的,它对于处理基元和泛型很有用,但也可以在编译类不一定代表运行时类时使用。 当涉及 ASM 字节码类转换器时尤其如此。
作为示例,我将使用Mixin框架。 以下代码将在运行时注入Foo
类。 我不会过多地详细说明这是如何工作的。
@Mixin(Foo.class)
public class MixinFoo {
public Foo bar() {
return (Foo) (Object) this;
}
}
的编译时类型this
似乎是MixinFoo
,但这种方法实际上会动态插入,所以在运行时,这将是Foo
。 编译器不知道这一点; 我们必须将其Foo
为Foo
,但这也会导致编译器错误。
先转换为Object
然后转换为Foo
(双重转换)将解决编译器的问题。 请记住,如果该方法未在运行时应用,这将导致运行时错误。
当然,这远远超出了入门 Java 的技能水平。
当然,虽然这只是避免了编译器错误,但必然会发生运行时错误。 此外,如果两个类在继承层次结构中,则无需向下转换到基类并再次向上转换。
即使在上面的问题中,最终也需要一个 API 方法来转换对象。
我最近遇到了这个问题并对其进行了使用。 我有一个包含 2 个整数值和一个字符串的对象数组。 当我打开它时,我需要将整数转换为 double 以进行一些除法。
问题是,当我将整数值转换为 double 时会出现错误,因为 Java 会自动将其检测为Integer
对象而不是int
原始类型,并且您无法从 Integer 转换为 double。
因此,解决方案是在进行除法之前使用(double)(int)value
。
所以总结一下,如果您将 int 存储为对象,并且您想将其转换为 double 以进行某些除法,java 将自动将其转换为 Integer 使除法成为不可能。
一个真正的用例是:
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
}
这是我遇到的一个简单的现实世界示例,您需要帮助编译器理解它应该在转换之前自动装箱:
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 。
是的,当您需要处理擦除的泛型和类型标记的交集时。
假设您想要一个在编译时和运行时都是类型安全的泛型函数。 所以你用一个类型标记声明它:
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
将是一个有效的类型标记,并且您不需要任何强制转换,更不用说奇怪的双重原始到假通用强制转换了。
也许这个问题是逻辑? 我的意思是,有一种方法可以计算某些东西(使用整数)并返回对象,以及使用将结果转换为字符串的方法,一般来说这个方法可以被覆盖几次,这样的功能在 Java 1.5 之前使用过你是否定义了泛型类(但没有泛型)和每个方法的返回是对象,因为有几个孩子,每个孩子都可以返回自己的类型,我不知道但可以回答,像这样双重铸造
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
}
}
尽管从程序员的角度来看,双重转换似乎是多余的,但至少了解语法的工作原理可以帮助您理解 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
,因此不会发生错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.