java - 使用泛型时,javac 编译器是否为每种类型创建不同的类?

标签 java class generics compiler-construction bytecode

如果我有一个泛型类,编译器是否会为我使用的每种类型创建一个不同的类?让我们考虑一下这个Class<T> 。如果我创建两个 Class<Integer> 类型的实例和Class<String> ,编译器是否创建了两个不同的类?

如果答案是否定的:扩展泛型类的类怎么可能继承不同类型(来自单个类)的相同方法或属性?

另一个问题:为什么我不能检查var instanceof Class<Integer>使用参数化类型而不是 ClassClass<?>

如果我尝试执行此操作,则会收到以下错误: “无法对参数化类型 Test<Integer> 执行 instanceof 检查。请使用形式 Test<?> 代替,因为进一步的泛型类型信息将在运行时被删除”

您能给我更多有关泛型的信息吗?

最佳答案

If I have a generic class, does the compiler create a different class for every type I use with it? Let's consider this Class. If I create two instances of type Class and Class, does the compiler create two different classes?

不,只有一个类,并且在字节码中,类型变量的所有出现都被有效地替换为它们的上限(通常为 Object ,但对于 U 形式的类型变量,可能是某些类型 T extends U )。这个概念称为类型删除,因为类型变量被有效地删除(并替换为它们的上限)。

If the answer is no: how it's possible that classes that extend a generic class can inherit the same method or attribute with a different type (from a single class).?

有趣的问题!假设您有两个实现 Comparator<T> 的不同类。一个实现 Comparator<String> ,另一个实现 Comparator<Integer>

Comparator<T> 定义了以下方法:

int compare(T p0, T p1)

那么两个不同的泛型实例如何使用不同的参数类型实现相同的方法呢?好吧,代码中实现的方法实际上并没有覆盖 Comparator.compare()Comparer.compare() 接受两个 Object 参数,但 Comparator<String>.compare() 接受两个 String 参数。它们不是同一个方法。那么,为什么它的行为类似于覆盖呢?因为编译器会为您生成一个隐藏的桥接方法。通过反编译器或反汇编器运行通用实现来亲自查看。以下是我自己使用 --show-synthetic 运行 Procyon 反编译器的输出:

public enum StringComparator implements Comparator<String> {
    ORDINAL {
        @Override
        public int compare(final String s1, final String s2) {
            if (s1 == null) {
                return (s2 == null) ? 0 : -1;
            }
            if (s2 == null) {
                return 1;
            }
            return s1.compareTo(s2);
        }

        @Override
        public /* bridge */ int compare(final Object x0, final Object x1) {
            return this.compare((String)x0, (String)x1);
        }
    },
    ...
}

第一个compare()方法是StringComparator类的作者编写的实际实现。第二种方法是隐藏的,由编译器生成。它的存在是为了“桥接”通用实现及其“删除”的定义,正是这个桥接方法实现了接口(interface)方法 Comparator.compare() 。请注意桥接方法如何使用类型转换来强制将 T 绑定(bind)到 String 。这在类型删除的世界中提供了一种安全措施。它确保以下情况产生异常:

class IntegerComparator implements Comparator<Integer> { ... }

// 'c' is a raw Comparator, or effectively a Comparator<Object>
// (note the lack of type arguments).
Comparator c = new IntegerComparator();
int result = c.compare(1, "hello");

上面的代码编译得很好,因为 Comparator.compare() 的原始形式接受两个 Object 参数。但在运行时,该调用将触发 ClassCastException ,因为 IntegerComparator 中的桥接方法将尝试将字符串“hello”转换为 Integer

Another question: why can't I check var instanceof Class<Integer> using parameterized type instead of Class or Class<?>?

由于具体泛型类型的所有实例共享同一个类,其中所有类型变量都已被删除,因此泛型类实例除了其原始类型之外没有任何身份感。它不知道实例化它所用的类型参数,因为该信息在编译期间已被删除。如果实例化 ArrayList<String> ,则生成的实例只知道它是 ArrayList 。在这种情况下,检查 instance instanceof ArrayList<String> 无法产生有意义的结果。由于此类检查无法可靠地1产生有意义的结果,因此不允许这样做。

<小时/>

1有趣的是,上面的 StringComparator 类的实例确实知道它实现了 Comparator<String> ,因为通用父类(super class)型信息保留在元数据中。

关于java - 使用泛型时,javac 编译器是否为每种类型创建不同的类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23697161/

相关文章:

C#:嵌套类的构造函数使 "inaccessible due to protection level"

java - Java 中的泛型继承

objective-c - Swift 函数覆盖 Objective-C 方法

java - 动态加载 GridPane 时显示 ProgressBar

java - 无法创建新的 Android Activity

java - 抽象类需要访问子类属性

ios - 我想要一个用于核心数据的通用 fetchedResultsController 来支持多个模型

javaPOS 将库添加到项目中

java - 从Java中的两个线程访问共享变量

c++ - 如何让代码存在于两个或多个非嵌套命名空间的范围内?