简体   繁体   English

Java通用继承。 重载或覆盖

[英]java generic inheritance. overloading or overriding

please compare two code snippets: 请比较两个代码段:

snippet1: 片段1:

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){

    }
} 

result( online compiler ): 结果( 在线编译器 ):

Main.java:13: error: reference to m is ambiguous, both method m(T#1) in Parent and method m(T#2) in Child match pm("1"); Main.java:13:错误:对m的引用不明确,Parent中的方法m(T#1)和Child中的方法m(T#2)都匹配pm(“ 1”); ^ where T#1,T#2 are type-variables: T#1 extends Object declared in class Parent T#2 extends CharSequence declared in class Child ^其中T#1,T#2是类型变量:T#1扩展了在父类中声明的对象T#2扩展了在子类中声明的CharSequence

snippet2(only one change!!!): snippet2(只有一个更改!!!):

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){

    }
} 

this code compiles good!( online compiler ) 这段代码编译好!( 在线编译器

please clarify this diference. 请澄清这个区别。

So the thing is here that what you have going on actually isn't overriding. 因此,这里发生的事情实际上是您无法进行的 Notice that if you add the @Override annotation to m() in Child you'll get a compilation error in both cases: 请注意,如果将@Override批注添加到Child m() ,则在两种情况下都会出现编译错误:

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

You can imagine Child actually looking like this: 您可以想象Child实际上看起来像这样:

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

    }

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

    }
} 

So when you try 所以当你尝试

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

The compiler sees the reference type Child<String> and sees that you actually have two methods that could work, as you have the m(String t) that was inherited from Parent<String> and m(T t) defined in Child , where T == String . 编译器会看到引用类型Child<String>并且实际上您有两种可行的方法,因为您拥有从Parent<String>m(T t)继承自Childm(String t) ,其中T == String Because now you effectively have two m(String t) methods, you'll have an ambiguous call. 因为现在您实际上有两个m(String t)方法,所以您将产生一个模棱两可的调用。

Now, if you have 现在,如果你有

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

The compiler sees the reference type Parent<String> and uses that to resolve the call to m(String) . 编译器将看到引用类型Parent<String>并使用该引用类型将对m(String)的调用解析。 As Parent<String> only has one such method defined, there is no ambiguous method call, so the code compiles. 由于Parent<String>仅定义了一个这样的方法,因此没有模棱两可的方法调用,因此代码可以编译。

This is one of the reasons why the @Override annotation should always be used -- there's no confusion about whether you are overloading a method or overriding it. 这就是为什么应该始终使用@Override批注的原因之一-对于您是重载方法还是重写方法,没有任何困惑。


Interesting fact: If memory serves this is actually one of the few times where generics are not erased. 有趣的事实:如果内存可用,这实际上是删除泛型的少数情况之一。 If you decompile Child you'll see the header 如果您反编译Child ,则会看到标题

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

The problem is that you are in fact not overriding the m(T) method of Parent . 问题在于您实际上并没有覆盖Parentm(T)方法。 So, you have two separate functions available: 因此,您有两个可用的独立功能:

  • Parent.m(String s) , because you forced the T in Parent to be String Parent.m(String s) ,因为您强制将Parent中的T设置为String
  • Child.m(T)

Imagine this declaration for Child: 想象一下这样的宣告:

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

Now, the Parent<T> gets to be Parent<Integer> , which means that the m method of Parent , now is a m(Integer i) method, while you have in your child class still a m(T) method available. 现在, Parent<T>成为Parent<Integer> ,这意味着Parentm方法现在是m(Integer i)方法,而您的子类中仍然有m(T)方法可用。 Now the difference is clear. 现在区别很明显。

You chose T to be a String , which coincidentally extends CharSequence , but that doesn't make it override the m(T) method of Parent. 您选择T作为String ,它同时扩展了CharSequence ,但是并没有使它覆盖Parent的m(T)方法。 So, changing your signature to: 因此,将您的签名更改为:

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

should work. 应该管用。 Now you are really overriding the method of Parent in Child. 现在,您真的覆盖了“亲子”的方法。

Your Child class gets around the rules of overriding (and goes into the realm of overloading) by declaring a method with a parameter that depends on a bounded type. 您的Child类通过声明带有依赖于有限类型的参数的方法来绕过重写规则(并进入重载领域)。 That type may be String or it may not be, so there is no clash with the method declared in the parent class when compiling the two. 该类型可能是String ,也可能不是,所以在编译两者时与父类中声明的方法没有冲突。

However, when compiling your Ideone class, by declaring the type argument as String in 但是,在编译Ideone类时,通过在以下位置将type参数声明为String

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

the type String is bound to the type parameter T which Child declares and uses in Child#m(..) . 类型String绑定到ChildChild#m(..)声明并使用的类型参数T Therefore the method appears as 因此,该方法显示为

void m(String t) {}

But so does Parent#m(..) because of the type argument String in the Child class declaration. 但是Parent#m(..)因为Child类声明中的类型参数String

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

As such, the Child class has two m(String) methods for that invocation. 这样, Child类具有用于该调用的两个m(String)方法。 The call is ambiguous. 通话不明确。

In

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

the Child class method is not visible since your reference is of type Parent . Child类方法不可见,因为您的引用的类型为Parent There is no ambiguity. 没有歧义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM