动态类型转换与泛型

huangapple 未分类评论49阅读模式
英文:

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 &lt;Integer, String&gt; box = new Box&lt;&gt;(30, &quot;Hallo&quot;);
            System.out.println(box);
            box.swapItems(box);
            System.out.println(box);
        }
    }

    public class Box &lt;F, S&gt; {    
        F first;
        S second;
    
        public Box(F first, S second) {
            this.first = first;
            this.second = second;
        }
    
        void swapItems(Box &lt;F,S&gt; box){
            F temp = box.first;
            box.first = (F) box.second;
            box.second = (S) temp;
        }
    
        @Override
        public String toString() {
           return &quot;Box{first=&quot; + first +&quot;, second=&quot; + second +&#39;}&#39;;
        }
    }
    
Outputs:

    &gt;&gt;&gt; Box {first = 30, second = Hallo}
    &gt;&gt;&gt; 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) &quot;Hello&quot;;` 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 &quot;30&quot;... 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 &lt;T,T&gt; 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>


huangapple
  • 本文由 发表于 2020年5月4日 21:36:18
  • 转载请务必保留本文链接:https://java.coder-hub.com/61593557.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定