java - 当传递给带有可变参数的第二个泛型方法时,我在泛型方法中丢失了变量的类型

标签 java generics

我想最好从我正在查看的行为开始:

public class genericTest {
    public static void main(String[] args) {
        String str = "5318008";

        printClass(str);              // class java.lang.String
        callPrintClass(str);          // class java.lang.String

        printClassVarargs(str);       // class java.lang.String
        callPrintClassVarargs(str);   // class java.lang.Object
    }

    public static <T> void printClass(T str) {
        System.out.println(str.getClass());
    }

    public static <T> void printClassVarargs(T ... str) {
        System.out.println(str.getClass().getComponentType());
    }

    public static <T> void callPrintClass(T str) {
        printClass(str);
    }

    @SuppressWarnings("unchecked")
    public static <T> void callPrintClassVarargs(T str) {
        printClassVarargs(str);
    }
}

查看 printClass()callPrintClass(),似乎一切正常。 callPrintClass() 采用通用参数并将其传递。 printClass() 通过其正确类型识别此变量,而不关心是谁发送参数,然后按预期执行并打印 java.lang.String

但是当我们尝试使用可变参数时,它就停止工作了。我希望 printClassVarargs() 能够识别它的参数是 String[] 类型,就像没有可变参数的方法识别其参数的类型一样。另请注意,如果我直接调用 printClassVarargs() 就不会发生这种情况(输出 String 非常高兴),但只有当它被 callPrintClassVarargs( ),它忘记了参数的类型并假定它正在获取一个 Object。我还意识到我必须在这里禁止编译器警告,这通常在我尝​​试转换泛型时出现,但我不确定那里到底发生了什么。

所以我的问题真的有两个。这种行为背后的原因是什么?这是类型删除的结果,还是 Java 处理数组的方式?其次,有什么办法解决这个问题吗?

当然这只是一个简单的例子。我不是试图以这种方式打印类名,而是最初在编写重载方法来连接数组时发现了问题。

最佳答案

Varargs 是语法糖,由编译器转换为给定类型的数组。 这意味着 method(Type arg...) 将变为 method(Type[] arg)

在 Java 中,您不能创建 Non-reifiable types 的数组(类型信息因删除而丢失的类型)。因此,printClassVarargs(T ... str) 等通用可变参数将转换为 printClassVarargs(Object[] str),有效地导致类型信息的删除。这就是您在测试中观察到的。

--- 编辑 ---

为了回答您关于 printClassVarargs(str)callPrintClassVarargs(str) 之间区别的问题(cfr 评论),我们可以查看您的测试类的字节码寻找所需的线索:

public Test();
  Code:
   0:   aload_0
   1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   ldc #16; //String 5318008
   2:   astore_1
   3:   aload_1
   4:   invokestatic    #18; //Method printClass:(Ljava/lang/Object;)V
   7:   aload_1
   8:   invokestatic    #22; //Method callPrintClass:(Ljava/lang/Object;)V
   11:  iconst_1
   12:  anewarray   #25; //class java/lang/String
   15:  dup
   16:  iconst_0
   17:  aload_1
   18:  aastore
   19:  invokestatic    #27; //Method printClassVarargs:([Ljava/lang/Object;)V
   22:  aload_1
   23:  invokestatic    #31; //Method callPrintClassVarargs:(Ljava/lang/Object;)V
   26:  return

public static void printClass(java.lang.Object);
  Code:
   0:   getstatic   #40; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   aload_0
   4:   invokevirtual   #46; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   7:   invokevirtual   #50; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   10:  return

public static void printClassVarargs(java.lang.Object[]);
  Code:
   0:   getstatic   #40; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   aload_0
   4:   invokevirtual   #46; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   7:   invokevirtual   #59; //Method java/lang/Class.getComponentType:()Ljava/lang/Class;
   10:  invokevirtual   #50; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   13:  return

public static void callPrintClass(java.lang.Object);
  Code:
   0:   aload_0
   1:   invokestatic    #18; //Method printClass:(Ljava/lang/Object;)V
   4:   return

public static void callPrintClassVarargs(java.lang.Object);
  Code:
   0:   iconst_1
   1:   anewarray   #3; //class java/lang/Object
   4:   dup
   5:   iconst_0
   6:   aload_0
   7:   aastore
   8:   invokestatic    #27; //Method printClassVarargs:([Ljava/lang/Object;)V
   11:  return

}

在 main#12 上观察,您的字符串 obj 的新 String[] 已创建,用作 printClassVarargs()callPrintClassVarargs() 的参数

在 main#19 上 printClassVarargs 被调用,创建的 String[] 作为参数。这导致 printClassVarargs 在运行时知道该对象的类型。此类型被保留。

在 main#23 上 callPrintClassVarargs 被调用,同样以创建的 String[] 作为参数。然后在 callPrintClassVarargs#1 上创建一个新数组。这一次,泛型类型声明中没有可用的类型信息,因此创建了一个新的 Object[]。 String[] 存储在此数组中,并在 callPrintClassVarargs#8 上传递给 printClassVarargs,它现在必须处理一个 Object[],其 componentType 是对象。

如您所见,当传递给 callPrintClassVarargs(T str) 的通用参数时,您的参数类型被删除了。

QED

关于java - 当传递给带有可变参数的第二个泛型方法时,我在泛型方法中丢失了变量的类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11501530/

相关文章:

java - Kafka Consumer 示例抛出 ClassNotFoundException

java - 泛型 - 定义 Map<MyClassA<?>, MyClassB<?>> 确保两者 ?是相同的?

c# - 需要一个接受实体 ID 并包含的通用 EF 方法

Java 7 的 fork/join 框架没有使用所有可用的 CPU 能力

java - commonj.work包中的Work接口(interface)是什么?

java - 调用 DISPOSE_ON_CLOSE 时启动函数

java - HttpSecurity 正则表达式匹配器未按预期工作

arrays - 如何检查 `Any` 类型的对象是一个实现某些协议(protocol)的具体类的数组

c# - 使用委托(delegate)参数创建通用实例

c# - 列表数据结构C#效率