如果在编译过程中将Java类型参数替换为其各自的边界,为什么以下代码不起作用?

时间:2018-11-01 06:06:49

标签: java generics overloading type-erasure

class World{

  <T extends Class1> World(TreeSet<? extends ClassA> set){
    *some code*
  }

  <T extends Class2> World(TreeSet<? extends ClassB> set){
    *some code*
  }

}

以上代码显示错误“名称冲突: World和 World具有相同的擦除”。为什么代码无法编译?更重要的是,为什么编译器说它们具有相同的擦除?我在该站点上发现的与该主题相关的所有问题都无法深入解释我的问题。

1 个答案:

答案 0 :(得分:0)

我在包含以下方法的类上使用Intellij Idea的Bytecode Viewer插件检查了问题:

public <A extends Calendar> void fn(ArrayList<? extends File> abc){  //line number:11
  A a=(A)new Object();  //line 12
}  //line number:13

下面是字节码查看器中与此方法相对应的字节码:

  // access flags 0x1
  // signature <A:Ljava/util/Calendar;>(Ljava/util/ArrayList<+Ljava/io/File;>;)V
  // declaration: void fn<A extends java.util.Calendar>(java.util.ArrayList<?extends java.io.File>)
public fn(Ljava/util/ArrayList;)V
  L0
    LINENUMBER 12 L0
    NEW java/lang/Object
    DUP
    INVOKESPECIAL java/lang/Object.<init> ()V
    CHECKCAST java/util/Calendar
    ASTORE 2
 L1
    LINENUMBER 13 L1
    RETURN
 L2
    LOCALVARIABLE this LMain; L0 L2 0
    LOCALVARIABLE abc Ljava/util/ArrayList; L0 L2 1
    // signature Ljava/util/ArrayList<+Ljava/io/File;>;
    // declaration: java.util.ArrayList<? extends java.io.File>
    LOCALVARIABLE a Ljava/util/Calendar; L1 L2 2
    // signature TA;
    // declaration: A
    MAXSTACK = 2
    MAXLOCALS = 3

看上面的第二行。尽管已注释掉,但保留了具有适当范围的类型参数。但是方法签名在编译后不包含任何类型参数(第4行)。我们在第10行中再次看到保留的类型参数信息用于转换,它对应于原始代码的第12行。因此得出的结论是类型参数并没有被其边界完全取代。您的编译文件将保留有关类型参数及其范围的信息。仅在编译器确定需要时才使用此保留的信息。出于某种原因,编译器认为方法(或构造函数)不需要在编译文件中的声明中包含这些信息(这解释了“相同擦除”错误部分)。当它不具有通用功能时,其原因与对Java版本的向后兼容性有关。因此,仅更改/添加/删除类型参数和/或其边界不会使方法或构造函数过载。


我知道我的回答是粗略的,但据我所知并非错误。请提出我在评论中缺少的任何内容,或发表您自己的答案。