尝试使用它们时,同一调用中的两个泛型方法是不是会导致编译错误?
Posted
技术标签:
【中文标题】尝试使用它们时,同一调用中的两个泛型方法是不是会导致编译错误?【英文标题】:Does two generics method at the same call can cause a compile error when trying to use them?尝试使用它们时,同一调用中的两个泛型方法是否会导致编译错误? 【发布时间】:2020-02-17 13:57:33 【问题描述】:我读过 Deital 的书:“Java How To Program”,在通用章节中这样写:“如果编译器没有找到与方法调用完全匹配的方法声明,但是 确实找到了两个或多个可以满足方法调用的方法,就会出现编译错误”。 有人可以给我一个例子,因为当我没有成功得到上述编译错误时。 我的代码:
public class Animal
public Animal ()
public String bark()
return "Animal";
public class Dog extends Animal
public String bark()
return "howhow";
public class DogSon extends Dog
public String bark()
return "I'm Dog Son";
public class Test
public static void main(String[] args)
Test t = new Test();
DogSon d = new DogSon();
System.out.println(t.helpMethod(d));
public <T extends Dog> String helpMethod(T dog)
System.out.println("aaa");
return dog.bark();
public <T extends Animal> String helpMethod(T animal)
return animal.bark();
由于没有完全获取子对象的方法,因此这里有两种通用方法可以适用。这不就是Deital所说的情况吗?
【问题讨论】:
不,擦除是不同的,一种方法比另一种更具体。 在这种情况下,匹配返回类型更合适的一个。如果没有更合适(具体),则会抛出错误,为清楚起见阅读***.com/questions/14694852/…。 【参考方案1】:不完全;在您的示例中,T dog
变体比T animal
变体更具体,因此,它是被选中的那个。
我不能保证我知道作者所指的内容。但我可以猜到:
public class Example<T>
public void m(T arg)
// At compile time, 'T', as it has a lower bound of Object,
// is treated as Object. Its signature therefore does not ***
// with the next m, and thus this can be compiled.
System.out.println("T-based");
public void m(Dog arg)
System.out.println("Dog-based");
new Example<Dog>().m(new Dog()); // this line causes a compiler error.
产生的错误是:
Animal.java:16: error: reference to m is ambiguous
new Example<Dog>().m(d);
^
both method m(T) in Example and method m(Dog) in Example match
where T is a type-variable:
T extends Object declared in class Example
1 error
所以,这里发生了什么:在编译时(Example.java 本身),这两个m
方法还不一定是模棱两可的;在大多数情况下,T 变体被视为等于其下限(即对象),在 java 中,您可以在同一类中拥有 m(Object arg)
和 m(Dog arg)
,没问题。
然而,在泛型之后,两个m
方法实际上确实发生了冲突:它们现在都将Dog
类型的任何东西作为参数。因此,不再可能在任何new Example<Dog>
上调用任一 m 方法。当涉及泛型并且类型参数可能重叠时,正确的解决方案是不要重载(当您有 2 个具有相同名称的不同方法时调用它)。大概,你书中那部分的作者试图告诉你为什么在这种情况下你不应该超载。
【讨论】:
我理解您的解释,但在您的示例中,有一种方法(获取 Dog 参数的方法)与方法调用完全匹配。在我从书中摘录的句子中写道:“如果编译器找不到与方法调用完全匹配的方法声明......”,但正如我所说,方法调用和方法声明完全匹配 是的,您粘贴的代码可以编译并运行。你跑了吗?它打印'aaa'。它不会产生编译器错误。 是的。但在我的评论中,我指的是您发布的代码【参考方案2】:您没有收到编译错误的原因是因为这两种方法
public <T extends Dog> String helpMethod(T t)
return t.bark();
public <T extends Animal> String helpMethod(T t)
return t.bark();
告诉编译器将T
分别解析为Dog
和Animal
。这实际上和写作一样
public String helpMethod(Dog t)
return t.bark();
public String helpMethod(Animal t)
return t.bark();
Dog
和 Animal
是两种不同的类型,尽管处于父子关系。编译器不会抱怨。
但是,如果您正在寻找编译错误,请尝试此操作
public <T extends Dog> String helpMethod(T t)
return t.bark();
public <T extends Dog> String helpMethod(T t)
return t.bark();
【讨论】:
【参考方案3】:你要明白的叫Erasure of Generic Methods
这意味着,如果方法的类型参数是未绑定,则将其转换为Object 或 它是绑定时的第一个绑定类。
Bound 意味着,-停留在您的示例中-您已经声明了类型参数 T 与另一个类的关系,就像您所做的那样:
// 1. method
public <T extends Dog> String helpMethod(T dog)
// 2. method
public <T extends Animal> String helpMethod(T animal)
T 在 1. 方法中绑定到 Dog,在 2. 方法中绑定到 Animal。
因此,在编译时,Java 将 T 更改为 1. 方法中的 Dog 和 2. 方法中的 Animal。 这里没有歧义,它们是两种不同的方法。
但是,如果你要声明一个 3. 方法:
// 3. method
public <T> String helpMethod(T dog)
那么T是unbound,你还没有声明T和另一个类之间的关系,所以在编译时,Java改变了T进入对象。
现在,如果你想声明一个 4. 方法:
// 4. method
public String helpMethod(Object dog)
会有一个编译错误,因为类型擦除期间的 3. 方法与您的 4. 方法具有完全相同的 method signature。 编译器无法决定您要调用哪一个,并引发错误。
考虑到上述情况,简短的回答是:
在您的代码中,您使用了两种不同的方法,没有歧义,因此不会发生错误。
如果您想查看该编译错误,您应该声明另一个泛型方法,该方法在编译后的签名将与您的类中现有的泛型/非泛型方法相同。
【讨论】:
你的解释很清楚。但是作者说的是调用方法。如果问题和你讲的一样,那讲方法调用有什么问题呢? 编译错误可能会在您尝试使用模棱两可的方法在此类上调用方法的那一行引发。如果您尝试编译类而不显式调用其方法,也会发生错误。 非常感谢,不幸的是我永远无法确定作者的确切意思。我觉得那里有些东西我错过了 别担心 :) 我不认为你错过了任何重要的事情。如果你理解我和其他人在这里写的内容,你就会很好地理解这个特定的主题。祝您在学习 Java 的过程中好运,如果您打算作为 Java 开发人员工作,我强烈建议您参加 OCA 考试,以及以下书籍:OCA:Oracle Certified Associate Java SE 8 Programmer I 学习指南:考试 1Z0-808作者:Scott Selikoff,Jeanne Boyarsky(平装本,2014 年),因为它很好地介绍了每个主题......然后在 Enthuware 测试自己,你会很高兴;-) 对不起,我有点困惑。您建议我参加 OCA 考试,我在哪里可以找到?在“Oracle Certified Associate Java SE 8 Programmer I Study Guide”一书中?以上是关于尝试使用它们时,同一调用中的两个泛型方法是不是会导致编译错误?的主要内容,如果未能解决你的问题,请参考以下文章