有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java转换为原始类型

我被一个代码卡住了,我不明白它为什么会工作。假设我创建了一个通用接口Foo<T>,如下所示:

interface Foo<T>{
   void set(T item);
}

然后我创建了一个名为Bar的类,它实现了Foo<String>,如下所示:

class Bar implements Foo<String>{ 
   @override
   public void set(String item){
      //useless body
   }
}

基于此,我们可以编写以下代码:

Bar bar = new Bar();
bar.set("Some string");
Foo rawFoo = (Foo) bar;
rawFoo.set(new Object()); // ClassCastException: Object cannot be cast to string

最后一行是我不太明白的。众所周知,当使用原始类型时,泛型参数类型转换为Object。 在这种情况下,代码将编译,我们可以将对象传递给set()方法。但是Java如何确定它必须在运行时将对象强制转换为字符串呢


共 (4) 个答案

  1. # 1 楼答案

    首先,您所做的只是提供对现有对象的引用,而不是创建新对象

    When compiling a class or interface that extends a parameterized class or implements a parameterized interface, the compiler may need to create a synthetic method, called a bridge method, as part of the type erasure process.

    类型擦除后,您的Foo变为:

    interface Foo{
       void set(Object item);
    }
    

    你的班级变成:

    class Bar implements Foo{ 
       @override
       public void set(String item){
          //useless body
       }
    }
    

    类型擦除后,方法签名不匹配

    因此,Bar set方法不实现Foo set方法

    为了解决这个问题并在类型删除后保留泛型类型的多态性,Java编译器生成一个桥接方法以确保子类型按预期工作。对于Bar类,编译器为set生成以下桥接方法:

    public void set(Object item){
        set((String) data);
    }
    

    https://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html

  2. # 2 楼答案

    事实证明,我的问题的答案是桥接方法。我目前正在读Maurice Naftalin和Philip Wadler的《Java泛型和集合》一书。我实际上读了一节关于桥接方法的内容,但我似乎没有仔细阅读。我很高兴所有的答案都有共同点。现在,我将再次阅读该部分

  3. # 3 楼答案

    Java中的泛型是编译时类型安全特性。因为它们是在1.5版中引入的,所以它们必须与以前的版本向后兼容。这允许您使用原始类型——一个不带类型参数的变量引用一个带类型参数的类

    在运行时,大多数类型参数信息被擦除,但不是全部。{a1}我们将在必要时插入编译器

    • Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
    • Insert type casts if necessary to preserve type safety.
    • Generate bridge methods to preserve polymorphism in extended generic types.

    在这里,编译器创建了一个桥接方法,以在运行时保留多态性,该方法采用Object,将其参数强制转换为String,然后调用实set(String)方法

    当您调用rawFoo.set(new Object());时,由于多态性,此桥接方法在Bar中调用。这是您在这里看到的隐式强制转换,显示为ClassCastException

  4. # 4 楼答案

    如果使用javap反编译Bar

    class Bar implements Foo<java.lang.String> {
      Bar();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    
      public void set(java.lang.String);
        Code:
           0: return
    
      public void set(java.lang.Object);
        Code:
           0: aload_0
           1: aload_1
           2: checkcast     #2                  // class java/lang/String
           5: invokevirtual #3                  // Method set:(Ljava/lang/String;)V
           8: return
    }
    

    void set(java.lang.Object)是一种合成桥方法。注意checkcast指令。等效的“真实”代码如下所示:

    public void set(Object object) {
      set((String) object);
    }
    

    编译器创建这个方法是为了让类在类型擦除下工作;做这项工作的实际方法是set(java.lang.String),桥接方法委托给它

    正是这个桥方法实际上重写了基类中的方法。用@Override注释声明的方法只是一个重载