擦除(erasure)为什么仍然允许覆盖/实现?

huangapple go评论78阅读模式
英文:

Why does erasure still allow overriding/implementation?

问题

乍一看,我认为以下内容是有道理的:

interface Test<T> {
    T getValue(T n);
}

class Impl implements Test<Integer>{
    public Integer getValue(Integer n){
        return n;
    }
}

并且它可以正确编译,所以一切似乎都很正常。

但后来我更深入地考虑了一下,在类型擦除的背景下,我觉得Test接口被擦除成了这样:

interface Test {
    Object getValue(Object n);
}

那么Impl如何能够继续实现Test呢?

英文:

At first glance I thought the following makes sense:

interface Test&lt;T&gt; {
    T getValue(T n);
}

class Impl implements Test&lt;Integer&gt;{
    public Integer getValue(Integer n){
        return n;
    }
}

And it compiles properly so everything seems A-OK.

But then I thought about it some more, in the context of erasure, and it seems to me that the Test interface gets erased to this:

interface Test {
    Object getValue(Object n);
}

So how is Impl still able to implement Test?

答案1

得分: 13

javac 实际上为此创建了桥接方法:

class Impl implements Test<Integer>{
    @Override public Integer getValue(Integer n){
        return n;
    }
}

编译为:

class Impl implements Test {
    public Integer getValue(Integer n) { // 不覆盖任何方法!
        return n;
    }
    @Override @Synthetic @Bridge public Object getValue(Object n) { 
        return this.getValue((Integer)n);
    }
}

注意:SyntheticBridge 并不是真正的注解,但是编译后的类文件会将这些方法标记为“合成的”和“桥接的”。

通过使用这些桥接方法,Java 确保如果你有一个 Impl,你可以调用 Impl#getValue(Integer)(如果你知道它实际上具有类型 Impl),或者你可以调用“通用”的 Test#getValue(Object)(如果你只知道它是一个 Test<?>)。

英文:

javac actually creates bridge methods for this:

class Impl implements Test&lt;Integer&gt;{
    @Override public Integer getValue(Integer n){
        return n;
    }
}

compiles to

class Impl implements Test {
    public Integer getValue(Integer n) { // overrides nothing!
        return n;
    }
    @Override @Synthetic @Bridge public Object getValue(Object n) { 
        return this.getValue((Integer)n);
    }
}

Note: Synthetic and Bridge are not real annotations, but the compiled class files do tag these methods as "synthetic" and "bridge".

By using these bridge methods, Java ensures that if you have an Impl, you can call Impl#getValue(Integer)Integer (if you know that it actually has type Impl), or you can call the "generic" Test#getValue(Object)Object, if you only know that it's a Test&lt;?&gt;.

答案2

得分: 0

以下是翻译好的部分:

这里有一种思维方式可以帮助您更好地理解泛型:将泛型视为严格的编译时保护机制。在运行时,没有任何这种保护机制(这是类型擦除的上下文)。为什么需要泛型做这个呢?为了支持 Java 5 之前的遗留代码。

编译器使用泛型类型信息(在您的 Test 类中的 &lt;Integer&gt;)来确保您的代码不会将错误的内容放入 getValue() 方法中,不会是字符串、浮点数、双精度数,只能是整数。因此,泛型为您提供了编译时保护和运行时保护的双重保障。

英文:

Here is one mindset to help you more about Generics: Think of generics as a strictly compile-time protection. None of this protection exists at runtime (it is the context of erasure). And why generics has to do this? To support legacy code before Java 5.

The compiler uses generic type information (&lt;Integer&gt; in your Test class) to make sure that your code doesn't put the wrong things into your getValue() method, not String, not Float, not Double, only Integer. Therefore, generics give you BOTH compile-time protection and runtime protection.

huangapple
  • 本文由 发表于 2020年7月25日 08:50:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/63083164.html
匿名

发表评论

匿名网友

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

确定