Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguity in method invocation, with arguments type of Character and float

public class Main {  
    static void over_Var(float a, Character... ab) {
        System.out.println("Method1");
    }
    static void over_Var(Character... ab) {
        System.out.println("Method2");
    }
    public static void main(String[] args) {
        System.out.println("Hello World");
        over_Var(1, 'm');
        over_Var('k', 'm');
    }
}

I'm getting error as this:

Main.java:19: error: reference to over_Var is ambiguous
    over_Var('k', 'm');
    ^
 both method over_Var(float, Character...) in Main and method over_Var(Character...) in Main 
 match 1 error

The code works fine if I use char instead of Character, or remove the line over_Var('k', 'm');

Why am I getting error like this?

like image 425
MRM Avatar asked Jul 15 '21 04:07

MRM


People also ask

What is ambiguous invocation in java explain with example?

Ambiguous Invocation. □ Sometimes there may be two or more possible. matches for an invocation of a method, but the. compiler cannot determine the most specific match. This is referred to as ambiguous invocation.

What is ambiguous method call?

This ambiguous method call error always comes with method overloading where compiler fails to find out which of the overloaded method should be used. Suppose we have a java program like below.

How do you remove ambiguity in java?

If the grammar has ambiguity then it is not good for a compiler construction. No method can automatically detect and remove the ambiguity but you can remove ambiguity by re-writing the whole grammar without ambiguity.

What is an ambiguity error?

Ambiguity errors occur when erasure causes two seemingly distinct generic declarations to resolve to the same erased type, causing a conflict.


Video Answer


2 Answers

Why over_Var(1, 'm'); is not ambiguous

1 is an integer. It can't be passed to a parameter of type Character or char directly (without casts), so the only option is the (float, Character...) overload. There does exist an int to float widening primitive conversion, which is permitted in an invocation context.

You might have thought that 1 could be converted to a Character because you can do that in assignment contexts.

Character a = 1;

However, this is purely because in the "Assignment Contexts" section of the JLS (see above link), there is this part that begins:

In addition, if the expression is a constant expression (§15.28) of type byte, short, char, or int:

[...]

The section for invocation contexts does not have this paragraph. So in reality, assignment contexts give constant expressions (like 1) special treatment, allowing them to be converted to smaller types than they actually are. Invocation contexts do not do this.

Why over_Var('k', 'm'); is ambiguous

In this case, both overloads are applicable. 'k' is a char, and the conversion from char to float (widening primitive conversion again) is allowed in an invocation context. The conversion from char to Character is also allowed in a loose invocation context.

If there are multiple applicable methods, the compiler chooses the most specific one. Which is more specific? Well, the second parameter of both calls would be Character, so we just need to consider the first parameter's type. One's float and the other's Character. Which one of these is more specific depends on their subtyping relationship. According to the subtyping rules, they are unrelated, so neither is more specific, and so you get a compiler error.

This is a simplification of the entire section of 15.12.2 of the spec, which I strongly encourage you to explore yourself :)

Why changing to char works

Unlike float and Character, float and char actually are related! Specifically:

  • double is a super type of float

  • float is a super type of long

  • long is a super type of int

  • int is a super type of char

  • int is a super type of short

  • short is a super type of byte

So char is a subtype of float, so over_Var(char...) is more specific than over_Var(float, char...), and hence will be preferred.

Some ways to resolve the ambiguity

To call (float, Character...), you can just cast:

over_Var((float) 'k', 'm');

To call (Character...), you can pass in a Character[].

over_Var(new Character[] {'k', 'm'});
like image 75
Sweeper Avatar answered Oct 24 '22 19:10

Sweeper


The problem here is not that you have variable length arguments. This is a The method X is ambiguous for the type Y error, explained in the link:

If more than one member method is both accessible and applicable to a method invocation … The Java programming language uses the rule that the most specific method is chosen.

java compiler doesn’t consider any of them to be more specific, hence the method ambiguous call error

First of all... why the connection between chars and floats?

That has to do with how chars work under the hood in Java.

Example:

System.out.println(200 - 'a'); // subtracting a char from 200
System.out.println(0 - 'a'); // subtracting a char from 0

Should Return:

103
-97

So what's the solution? You indicated one: change Character to char. But why does that work? You have stumbled upon very interesting Java behavior. Take this code for example:

public class Main {  
    static void over_Var(float a, Character b)   {
        System.out.println("Method1");
    }
    static void over_Var(char a, Character b) {
        System.out.println("Method2");
    }
    static void over_Var(Character a, Character b) {
        System.out.println("Method3");
    }
    public static void main(String[] args) {
        over_Var('k','m');
    }
}
  • This will have an error due to ambiguity between the char and Character.

  • Comment out the first function. It will still have the error due to ambiguity between char and Character

  • Comment out the second function. It will fail due to ambiguity between float casted as Character, and Character

  • Comment out the third function. It will print Method2. It knows to prioritize char over float!

This appears... to violate transitivity. char + Character = ambiguity. Character + float = ambiguity. But char + float = non-ambiguous? I will leave you with the word "strange".

Edit: the reason for this (see Sweeper's better answer), is that it knows to prioritize char over float because they are related types; char is a subtype of float. On the other hand, Character is not related. (In other words, the reason for the apparent lack of "transitivity" is that specificity can only be assessed in the context of the types being compared.)

like image 23
Sean AH Avatar answered Oct 24 '22 18:10

Sean AH