【问题标题】:java generic inheritance. overloading or overridingjava泛型继承。重载或覆盖
【发布时间】:2014-06-13 22:34:50
【问题描述】:

请比较两个代码sn-ps:

sn-p1:

class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        Child<String> p =new Child<String>();
        p.m("1");
    }
}
class Parent <T>{
    void m(T t){

    }
} 
class Child<T extends CharSequence> extends Parent<String>{
    void m(T t){

    }
} 

结果(online compiler):

Main.java:13: 错误:对 m 的引用不明确,两种方法都是 m(T#1) 在 Parent 和方法 m(T#2) in Child match p.m("1"); ^ 在哪里 T#1,T#2 是类型变量: T#1 扩展在 Parent 类中声明的 Object T#2 扩展在 Child 类中声明的 CharSequence

sn-p2(只有一个变化!!!):

class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        Parent<String> p =new Child<String>();
        p.m("1");
    }
}
class Parent <T>{
    void m(T t){

    }
} 
class Child<T extends CharSequence> extends Parent<String>{
    void m(T t){

    }
} 

这段代码编译得很好!(online compiler)

请澄清这一区别。

【问题讨论】:

  • 只是想知道:这行得通吗:class Child&lt;T extends CharSequence&gt; extends Parent&lt;T&gt;{
  • @Martijn Courteaux 真的很有效)))泛型让我的大脑崩溃

标签: java generics overloading overriding type-erasure


【解决方案1】:

所以事情就在这里,你所做的实际上 不是 压倒一切。请注意,如果您在Child 中将@Override 注释添加到m(),您将在两种情况下都会收到编译错误:

Main.java:22: error: method does not override or implement a method from a supertype
    @Override

你可以想象Child实际上是这样的:

class Child<T extends CharSequence> extends Parent<String>{
    void m(T t){

    }

    void m(String t) { // <-- This was the method inherited from Parent

    }
} 

所以当你尝试

Child<String> p =new Child<String>();

编译器看到引用类型Child&lt;String&gt; 并看到您实际上有两个 可以工作的方法,因为您定义了从Parent&lt;String&gt;m(T t) 继承的m(String t)Child,其中T == String。因为现在您实际上有两个 m(String t) 方法,所以您将有一个模棱两可的调用。

现在,如果你有

Parent<String> p =new Child<String>();

编译器看到引用类型Parent&lt;String&gt; 并使用它来解析对m(String) 的调用。由于Parent&lt;String&gt;只定义了一个这样的方法,所以没有模棱两可的方法调用,所以代码可以编译。

这就是为什么应该总是使用@Override注解的原因之一——对于你是重载一个方法还是重写它没有混淆。


有趣的事实:如果没有记错的话,这实际上是少数删除泛型的情况之一。如果你反编译Child 你会看到标题

class Child<T extends java.lang.CharSequence> extends Parent<java.lang.String> 

【讨论】:

  • 你能解释一下为什么它会超载(而不是覆盖)吗?我准备考试了,通过了IDE就无法使用了。
  • @gstackoverflow 因为您有两个具有不同参数的同名方法。您从Parent&lt;String&gt; 继承m(String t),并在Child&lt;T extends CharSequence&gt; 中声明m(T t)。为了覆盖,您需要让覆盖方法的签名成为原始方法签名的子签名。精确的规则在 JLS 部分 8.4.8.1 中定义,但通常“相同签名”是一个不错的近似值。
  • 因此覆盖或重载 - 它在编译时解决。这对 String Integer MyCustomClass 和 T 的规则相同。我的更改 - 它是运行时 hack。
  • @gstackoverflow 我不太确定你在这里想说什么......但是由于这是重载,它在编译时绑定,这就是你得到编译时错误的原因.我不太清楚你所说的运行时黑客是什么意思......因为编译器使用变量的 reference 类型来确定哪些方法可用于绑定。
【解决方案2】:

问题在于您实际上并没有覆盖Parentm(T) 方法。因此,您有两个单独的函数可用:

  • Parent.m(String s),因为您将 Parent 中的 T 强制为 String
  • Child.m(T)

想象一下这个对 Child 的声明:

class Child<T extends CharSequence> extends Parent<Integer>

现在,Parent&lt;T&gt; 变为Parent&lt;Integer&gt;,这意味着Parentm 方法现在是m(Integer i) 方法,而您的子类中仍然有m(T) 方法可用的。现在区别很明显了。

您选择了T 作为String,它巧合地扩展了CharSequence,但这不会使其覆盖Parent 的m(T) 方法。因此,将您的签名更改为:

class Child<T extends CharSequence> extends Parent<T>

应该可以。现在你真的在 Child 中重写了 Parent 的方法。

【讨论】:

  • 是编译时分辨率吗?
  • 是的,是编译时问题。编译无法在这两种方法之间进行选择。
【解决方案3】:

您的Child 类通过声明一个带有取决于有界类型的参数的方法来绕过覆盖规则(并进入重载领域)。该类型可能是String,也可能不是,所以编译两者时与父类中声明的方法没有冲突。

但是,在编译 Ideone 类时,通过将类型参数声明为 String in

Child<String> p = new Child<String>();

类型String 绑定到Child 声明并在Child#m(..) 中使用的类型参数T。因此该方法显示为

void m(String t) {}

Parent#m(..) 也是如此,因为Child 类声明中的类型参数String

class Child<T extends CharSequence> extends Parent<String> {

因此,Child 类有两个用于该调用的 m(String) 方法。调用是模棱两可的。

Parent<String> p =new Child<String>();
p.m("1");

Child 类方法不可见,因为您的引用是 Parent 类型。没有歧义。

【讨论】:

    猜你喜欢
    • 2011-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多