Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

358
Views
Why is this Java generic method call ambiguous when only one method is valid when separate?

I am trying to understand why the compiler is unable to resolve the bar method call. I would expect bar(Xyz::new) to always select bar(Supplier) as bar(T extends Xyz) can never match due to the upper bound on Xyz.

public <T extends Xyz> void foo(T s) {}
public <T extends Xyz> void bar(T s) {}
public <T extends Xyz> void bar(Supplier<T> s) {}

public void example() {
    foo(Xyz::new); // not valid (does not extend Xyz)

    bar((Supplier<Xyz>) Xyz::new); // valid (explicitly a Supplier)
    bar(Xyz::new); // ambiguous - but only one method is valid?
}

public static class Xyz {}

If bar(T) is not applicable, even when alone (as shown with foo(T)), then surely the only option is bar(Supplier) making this a non-ambiguous overload.

Why is the bar call ambiguous, especially when the foo and bar(T) calls are not valid resolutions themselves?

Runnable example of above code: https://www.jdoodle.com/ia/kqP

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

You're right that a smarter compiler should be able to resolve this unambiguously.

The way Java resolves method invocations is complex. It's defined by the JLS, and I make it 7500 words purely to determine how to resolve a method. Pasted into a text editor, it was 15 pages.

The general approach is:

  1. Compile-Time Step 1: Determine Type to Search (no issue here)
  2. Compile-Time Step 2: Determine Method Signature
    1. Identify Potentially Applicable Methods
    2. Phase 1: Identify Matching Arity Methods Applicable by Strict Invocation
    3. Phase 2: Identify Matching Arity Methods Applicable by Loose Invocation
    4. Phase 3: Identify Methods Applicable by Variable Arity Invocation
    5. Choosing the Most Specific Method
    6. Method Invocation Type
  3. Compile-Time Step 3: Is the Chosen Method Appropriate?

I don't understand anywhere close to all of the details and how it pertains to your specific case. If you care to dive into it then I've already linked the full spec. Hopefully this explanation is good enough for your purposes:

Ambiguousness is determined at step 2.6, but there is still a further appropriateness check at step 3. Your foo method must be failing at step 3. Your bar method never makes it that far because the compiler still considers both methods to be valid possibilities. A human can make the determination that the non-appropriateness resolves the ambiguity, but that's not order the compiler does things. I could only speculate why - performance might be a factor.

Your code is operating at the intersection of generics, overloading and method references, all three of which were introduced at different times; it's not massively surprising to me that the compiler would struggle.

over 4 years ago · Santiago Trujillo Report

0

Your problem is mostly a problem of type inference that a problem of ambiguous method:

public <T extends Xyz> void bar(T s) {} // bar(Xyz)
public void bar(String s) {}
public <T extends Zyx> void bar(T s) {} // bar(Zyx)
public <T extends Xyz> void bar(Supplier<T> s) {}
public static class Xyz {}
public static class Zyx {}

If you use:

    bar(new Xyz());  // ok
    bar("a");   // ok
    bar(new Zyx());   // ok
    bar((Supplier<Xyz>) Xyz::new); // ok
    bar(Xyz::new); // ambiguous

You get this error (tried with Java 17) which is not about the lambda, but about the type T: cannot infer type-variable(s) T

  both method <T#1>bar(T#1) in Example and method <T#2>bar(Supplier<T#2>) in Example match
  where T#1,T#2 are type-variables:
    T#1 extends Zyx declared in method <T#1>bar(T#1)
    T#2 extends Xyz declared in method <T#2>bar(Supplier<T#2>)
Example.java:18: error: incompatible types: cannot infer type-variable(s) T

Java is not smart enough to find the concrete type T is this case, and you have to help it:

Example.<Xyz>bar(Xyz::new);

I tried to look into the JLS, driven by Michael answer, and the section that should better answer your question is the 18.5.1. Invocation Applicability Inference.

I had the same kind of errors frequently occurring with Java 7 and Collections:

public static <T extends Zyx> void bar(java.util.List<T> s) {} // bar(Zyx)
public static <T extends Zyx> void bar(T s) {} // bar(List)
bar(new Zyx()); 
bar(java.util.Collections.emptyList()); 

The worse of it being that Eclipse was having no trouble, while javac failed.

I suppose that in the case of lambdas, the compiler does not infer the type T from the "Xyz".

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!