简体   繁体   English

为什么我会在这段代码中收到歧义错误?

[英]Why do I get an ambiguity error in this code?

Let's say we have these 3 classes:假设我们有这 3 个类:

class A { }
class B extends A { }

public class App {
    static void f(int i, A a) { }
    static void f(float j, B b) { }
   
    static public void main() {
        int i = 0;
        B b = new B();
        App.f(i, b);
    }
}

This produces the error:这会产生错误:

App.java:11: error: reference to f is ambiguous
        App.f(i, b);
           ^
  both method f(int,A) in App and method f(float,B) in App match
1 error

Why does it not choose the type f(int, A) since i is an integer?为什么它不选择f(int, A)类型,因为i是 integer?

It is ambiguous because of two reasons:之所以模棱两可,原因有二:

  • both overloads are applicable , and;两种重载都适用,并且;
  • neither overload is more specific than the other两个重载都不比另一个更具体

Notice that both the f(int, A) overload and the f(float, B) overload can be called with the parameters (i, b) , since there is an implicit conversion from int to float , and an implicit conversion from B to A .请注意, f(int, A)重载和f(float, B)重载都可以使用参数(i, b)调用,因为存在从intfloat的隐式转换,以及从B到的隐式转换A

What happens when there are more than one applicable method?当有不止一种适用的方法时会发生什么? Java is supposed to choose the most specific method. Java应该选择最具体的方法。 This is described in §15.12.2.5 of the language spec.这在语言规范的§15.12.2.5中有描述。 It turns out that it is not the case that one of these overloads are more specific than the other.事实证明,这些重载中的一个并非比另一个更具体。

One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true:一个适用的方法 m1 比另一个适用的方法 m2 更具体,用于使用参数表达式 e1、...、ek 的调用,如果以下任何一项为真:

  • m2 is generic [...] m2 是通用的 [...]

  • m2 is not generic, and m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ n, n = k). m2 不是泛型的,m1 和 m2 可以通过严格或松散调用来应用,其中 m1 有形参类型 S1,...,Sn,m2 有形参类型 T1,...,Tn,类型 Si 更多对于所有 i (1 ≤ i ≤ n, n = k),参数 ei 比 Ti 更具体。

  • m2 is not generic, and m1 and m2 are applicable by variable arity invocation [...] m2 不是通用的,并且 m1 和 m2 可通过变量 arity 调用应用 [...]

Only the second point applies to the two overloads of f .只有第二点适用于f的两个重载。 For one of the overloads to be more specific than the other, every parameter type of one overload has to be more specific than the corresponding parameter type in the other overload.为了使其中一个重载比另一个更具体,一个重载的每个参数类型都必须比另一个重载中的相应参数类型更具体。

A type S is more specific than a type T for any expression if S <: T ( §4.10 ).对于任何表达式,如果 S <: T ( §4.10 ),类型 S 比类型 T 更具体。

Note that"<:" is the subtyping relationship.注意“<:”是子类型关系。 B is clearly a subtype of A . B显然是A的子类型。 float is actually a supertype (not subtype!) of int . float实际上是int的超类型(不是子类型!)。 This can be derived from the direct subtyping relations listed in §4.10.1 .这可以从§4.10.1中列出的直接子类型关系推导出来。 Therefore, neither of the overloads is more specific than the other.因此,没有一个重载比另一个更具体。

The language spec goes on to talk about maximally specific methods, which doesn't really apply to f here.语言规范继续讨论最大限度地具体的方法,这并不真正适用于f这里。 Finally, it says:最后,它说:

Otherwise, the method invocation is ambiguous, and a compile-time error occurs.否则,方法调用不明确,出现编译时错误。

More Examples更多示例

static void f(int x) {}
static void f(float x) {}

when called with an int are not ambiguous because the int overload is more specific.当用int调用时并不模棱两可,因为int重载更具体。

static void f(int x, B a) {}
static void f(float x, A a) {}

when called with argument types (int, A) are not ambiguous because the (int, B) overload is more specific.当使用参数类型(int, A)调用时,它并没有歧义,因为(int, B)重载更具体。

static void f(int x, A a) {}
static void f(float x, A a) {}

when called with argument types (int, A) are not ambiguous because the (int, A) overload is more specific.当使用参数类型(int, A)调用时,它并没有歧义,因为(int, A)重载更具体。 Note that the subtyping relationship is reflexive (ie A is a subtype of A ).请注意,子类型关系是自反的(即AA的子类型)。

Note that for calling an overloaded method in java:请注意,在 java 中调用重载方法:

Priority Order for Primitive types:原始类型的优先顺序:

Same type > Auto Widening > Boxing > Upcasting(Parent Class) > Super Class相同类型 > 自动加宽 > 拳击 > 向上投射(父类) > Super Class

Priority Order for Reference types:参考类型的优先顺序:

Same type > Upcasting(Parent Class) > Super Class > Unboxing相同类型 > Upcasting(父类) > Super Class > 拆箱

Explanation:解释:

    // you have here a method who accept an int = same Type 
    //  f(int i, A a) { } and this method can accept the other parameter 
    //  because B is a subclass of A
    int i = 0;
    // But You have a method who accept a B reference = same Type 
    // f(float j, B b) { } and this method can accept the other parameter 
    // because float is actually a supertype of int as mentionned by Sweeper
    B b = new B();

 // So calling the method with an int and a B reference 
 // will confuse the compiler because both of the two methods
 // can accept the other parameter
    App.f(i, b);

Two things:两件事情:

  • Type widening (1st parameter)类型扩展(第一个参数)
  • Inheritance (2nd parameter) Inheritance(第二个参数)

Type widening : float is "wider" type than int therefore passed int value can be easily packed into bigger float box. 类型扩大float是比int更“宽”的类型,因此传递的int值可以很容易地打包到更大的float框中。 It will not work in case, when we would like to "pack" float into int , as we can potentially lost floating point.如果我们想将float “打包”到int中,它将不起作用,因为我们可能会丢失浮点。 In provided case, having an int we can potentially pass it two both functions.在提供的情况下,拥有一个int我们可以潜在地将两个函数都传递给它。

fFloat(float f){}
int intValue = 221;
fFloat(intValue); // inside f, intValue is treated as 221.0

fInt(int i){}
float floatVal = 221.221;
fInt(floatVal); // what should compiler do with remaining .221 part?

Inheritance : A is a base class, while B is one of subclasses of A. So, when we declare method parameter as a base class, we can provide there both base class and its instance (but we will be able to use B b instance inside f(int i, A a) like type A . Hence, we can pass B b = new B(); to both methods as well. Inheritance : A is a base class, while B is one of subclasses of A. So, when we declare method parameter as a base class, we can provide there both base class and its instance (but we will be able to use B b instance在f(int i, A a)内部,类似于A类型。因此,我们也可以将B b = new B();传递给这两种方法。

class A {
public void f() {}
}

class B extends A {
public void g() {}
}

fA(A a){}
B bInst = new B();
fA(bInst); // you can refer only to f() method, not g()

fB(B b){}
A aInst = new A();
fB(aInst); // you cannot do this, as subclass can have some additional
// stuff, that superclass does not have

//but even
A aInstBImpl = new B();
fB(aInstBImpl); // will not work without explicit casting as it is
// kind of A instance with B implementation - here polymorphism comes into action

Since all 2 parameters (from 2 possible) can suit to both methods without any explicit "action taken", the ambiguity error arises.由于所有 2 个参数(从 2 个可能)都可以适用于这两种方法,而无需任何明确的“采取行动”,因此会出现歧义错误。

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

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