JDK-6775110 : Reference method ambiguous on overrided protected method w. generic params
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 6u10
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2008-11-21
  • Updated: 2010-04-06
  • Resolved: 2008-12-03
Related Reports
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_10"
Java(TM) SE Runtime Environment (build 1.6.0_10-b33)
Java HotSpot(TM) Client VM (build 11.0-b15, mixed mode, sharing)

java version "1.5.0_16"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_16-b02)
Java HotSpot(TM) Client VM (build 1.5.0_16-b02, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Versione 5.1.2600]
(Italian)

A DESCRIPTION OF THE PROBLEM :
I found a wrong compile error "reference to method is ambiguous" when calling a method with a row parameter that overrides a protected method with a generic parameter, and there is another method equally accessible and applicable, but less specific.
If the overridden method is declared public instead of protected, no problem arises. The abstract modifier has no relevance.
This particular situation can happen when trying to generify a class, leaving unchanged all existing subclasses.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Try to compile the supplied source code (each class in its own compilation unit).

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
All should compile fine.
Specifically, call to methodAB(BVO) in CMgr.methodC() should be resolved as CMgr.methodAB(BVO), as this is the most specific method accessible and applicable.
ACTUAL -
Compiler error in CMgr.methodC():
- reference to methodAB is ambiguous, both method methodAB(AVO<?>) in ABMgr and method methodAB(BVO) in CMgr match

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class AVO<Type> {
}

public class BVO<Type> extends AVO<Type> {
}

public abstract class ABMgr {
    public abstract void methodAB(AVO<?> vo);
    protected abstract void methodAB(BVO<?> vo);
}

public abstract class CMgr extends ABMgr {
    @Override
    public void methodAB(BVO vo) {
    }
    public void methodC() {
        methodAB(new BVO()); //compiler error
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Make ABMgr.methodAB(BVO<?>) public.
But that's not always possible, e.g. if some existing code override it with protected accessibility.

Comments
EVALUATION Closing this bug as not a defect for the reasons listed above. Refer to related CR 6775192 for the protected vs. public issues.
03-12-2008

EVALUATION Since JLS 15.12.2.5 DOES use "strict" subtyping, it's a fact that BVO is not a subtype of AVO<?>. The algorithm continues and, since the signatures are not and never will be override-equivalent, decides the call is ambiguous. The type hierarchy according to "strict" subtyping is effectively: AVO | \ | \ | \ | BVO AVO<?> | | \ | | \ | | \ | | BVO<?> AVO<..> | \ / \ / \ / \ / BVO<..> AVO<?> is really the root, and AVO and BVO exist on the side. In "non-strict" subtyping, where methodAB(BVO) is preferred to methodAB(AVO<?>), BVO is pushed deeper into the hierarchy, making it harder to deprecate raw types later: AVO | | AVO<?> | \ | BVO | | | | | | | BVO<?> AVO<..> | \ / \ / \ / \ / BVO<..> Would the caller prefer ABMgr.methodAB(AVO<?>) or CMgr.methodAB(BVO) to be invoked when an object of class BVO is passed at runtime? Pre-generics, when the choice was methodAB(AVO) v. methodAB(BVO), the latter was rightly selected at compile-time. It's quite strange that generics were allowed to cause methodAB(AVO<?>) to be selected when the caller is recompiled, since that method might reasonably have different behavior. The choice was evidently made to keep a simpler type hierarchy (the first diagram) even if it means restricting migration compatibility, such that advanced migrations like this one can't be made. In other words, migration compatibility stops at the point where the superclass's signature is not override-equivalent with the subclass's signature. This seems like fair, long-term reasoning to me.
02-12-2008

EVALUATION This bug is a result of some problems during overload resolution. During overload resolution, two applicable methods are found, namely ABMgr.methodAB(AVO<?>) and CMgr.methodAB(BVO) - since CMgr.methodAB(BVO) overrides (raw overriding) ABMgr.methodAB(BVO<?>). In this case, javac should apply the algorithm described in JLS, section 15.12.2.5 (choosing the most specific method). m1 = ABMgr.methodAB(AVO<?>) m2 = CMgr.methodAB(BVO) Is m1 most specific? True iff AVO<?> <: BVO (4th bullet), false. Is m2 most specific? True iff BVO <: AVO<?> (4th bullet again) Here we have two possible answers: 1) since 15.12.2.5 uses strict subtyping (thus not allowing any unchecked conversion) it might be tempting to consider that BVO is not a subtype of AVO<?>. If that's true, this means that m2 is not most specific, so we end up in a scenario where neither method is most specific. IN this case we should apply the last two bullets of 15.12.2.5: -are m1 and m2 override-equivalent? the answer is: no they aren't (their argument types differ). This in turn implies that the call to methodAB(BVO) is ambiguous. 2) The other possibility is to allow unchecked subtyping; as a result we would have that BVO is an unchecked subtype of AVO<?> - because of that we have that m2 is the most specific method and should thus be selected as the only applicable method during overload resolution. This basically reduces to this very question: should unchecked conversion be allowed during overload resolution? Note: the submitter notices that changing the modifier of ABMgr.methodAB(BVO<?>) from public to protected solves this problem. This is true, but unfortunately only because of another javac bug, described in both 6400189 and 6775192. In particular, by declaring ABMgr.methodAB(BVO<?>) as public, javac first chooses ABMgr.methodAB(BVO<?>) as the most specific between ABMgr.methodAB(BVO<?>) and CMgr.methodAB(BVO) (which is wrong, javac shouldn't even apply overload resolution to overriding methods) and then chooses ABMgr.methodAB(BVO<?>) as the most specific between ABMgr.methodAB(BVO<?>) and ABMgr.methodAB(AVO<?>) - basically the overriding method is not considered - which is equally wrong.
24-11-2008