英文:
Dynamic Casting With Generics
问题
以下是您提供的代码的翻译部分:
以下代码,编译并且执行没有错误:
public class Main {
public static void main(String[] args) {
Box<Integer, String> box = new Box<>(30, "Hallo");
System.out.println(box);
box.swapItems(box);
System.out.println(box);
}
}
public class Box<F, S> {
F first;
S second;
public Box(F first, S second) {
this.first = first;
this.second = second;
}
void swapItems(Box<F, S> box){
F temp = box.first;
box.first = (F) box.second;
box.second = (S) temp;
}
@Override
public String toString() {
return "Box{first=" + first + ", second=" + second + '}';
}
}
输出:
>>> Box {first = 30, second = Hallo}
>>> Box {first = Hallo, second = 30}
乍一看这没问题,但实际上有很多问题(对我而言)。事实上,发生了多件非常奇怪的事情。
1. `F temp = box.first;` 这行将 `temp` 设置为一个整数。目前还好。
2. `box.first = (F) box.second;` 这行,然而,**不应该被允许执行**,但实际上却执行了。确切地说,我们正在进行类似这样的转换:`int a = (int) "Hello";` 而这显然是不对的。所以我的问题是,为什么这里能工作?为什么我们可以在运行时动态地改变 `box.first` 的类型,而在其他情况下这是被禁止的呢?
3. `box.second = (S) temp;` 更糟糕的是,这行的效果类似于 `string s = (String) 30`,虽然在语法上是错误的,但至少在直觉上可以理解为 30 可以变成 "30"……然而,实际发生的是 `box.second` 的类型变成了整数,并且 30 被赋值给它。这对我来说毫无意义,一个整数被强制转换为字符串,结果接收变量的类型被动态地更改为整数?
此外,在运行此代码后,可以继续像什么都没发生一样使用 `box`,只有在尝试执行假定其变量类型的操作时,才会出现类转换异常(正如预期的那样)。
我知道避免这种情况的解决方案是将 `swapItems` 方法设置为静态,并使用另一个泛型类型变量来强制传递参数的箱子类型的关系(例如,`T void swapItems(Box<T, T> box)`),但问题是:为什么第 2 和第 3 行(使用上述符号)能够工作?在底层发生了什么,让编译器/JVM 认为这是可接受的行为?我是否漏掉了什么?
**编辑:**
根据上面链接的重复答案,可以总结对我问题的答案:
*Java 泛型只是在对象强制转换上的语法糖。*
<details>
<summary>英文:</summary>
The following code, compiles and executes without error:
public class Main {
public static void main(String[] args) {
Box <Integer, String> box = new Box<>(30, "Hallo");
System.out.println(box);
box.swapItems(box);
System.out.println(box);
}
}
public class Box <F, S> {
F first;
S second;
public Box(F first, S second) {
this.first = first;
this.second = second;
}
void swapItems(Box <F,S> box){
F temp = box.first;
box.first = (F) box.second;
box.second = (S) temp;
}
@Override
public String toString() {
return "Box{first=" + first +", second=" + second +'}';
}
}
Outputs:
>>> Box {first = 30, second = Hallo}
>>> Box {first = Hallo, second = 30}
While on a first look this is alright, it is very much not (to me). In fact, multiple things happens which are very weird.
1. `F temp = box.first;` this lines sets `temp` to be an Integer. So far so good
2. `box.first = (F) box.second;` this line, however, **should NOT be allowed to execute**, except it does. Quite literally, we are casting the respective of: `int a = (int) "Hello";` and this is evidently not ok. So my question is why is it working here? Why do we get the type of `box.first` to dynamically change at runtime, whereas this is forbidden in other situations?
3. `box.second = (S) temp;` even worse, this line is doing the respective of `string s = (String) 30`, and while this is syntactically wrong, it makes at least intuitive sense as 30 could become "30"... however, what happens is that the type of `box.second` becomes Integer and 30 is assigned to it. This makes no sense to me, an integer is being cast to string and as a result the type of the receive variable is dynamically changed to Integer?
Moreover, after this code is run one can continue using box as if nothing happened, only when you try to perform operations assuiming the type of its variable one incours in classcast exceptions (as expected).
I know that the solution to avoid this is to make `swapItems` static and use another generic type variable to force the relationship of types of the box passed as parameters (e.g. `T void swapItems(Box <T,T> box){..}` but the question here is: why are lines 2 and 3 (with notation used above) working? What is happening beneath that makes the compiler/JVM think this was an acceptable execution of lines? Am I missing something ?
**EDIT:**
From duplicated answers linked above, one can summarize the answer to my questions as:
*Java generics are simply syntactic sugar on casting Objects.*
</details>
专注分享java语言的经验与见解,让所有开发者获益!
评论