Open In App

Ambiguities in Java

Improve
Improve
Like Article
Like
Save
Share
Report

The ambiguities are those issues that are not defined clearly in the Java language specification. The different results produced by different compilers on several example programs support our observations. Here we will be discussing in the following order.

  • Ambiguity method in method overloading
    • Methods with only Varargs parameters
    • Methods with Varargs along with other parameters
  • Ambiguity in multiple inheritances
    • Diamond Problem

Type 1: Ambiguity method in method overloading 

When you overload methods, you risk creating an ambiguous situation of which one is in which the compiler cannot determine which method to use. For example, consider the following overloaded computeBalance() method declarations:

public static void computeBalance(double deposit)
public static void computeBalance(double withdrawal)

If you declare a double variable named myDeposit and make a method call such as computeBalance(myDeposit);, you will have created an ambiguous situation. Both methods are exact matches for your call. You might argue that a call using a variable named myDeposit “seems” like it should go to the version of the method with the parameter named deposit, but Java makes no assumptions based on variable names. Each version of computeBalance() could accept a double, and Java does not presume which one you intend to use.

Ambiguity Errors

The inclusion of generics gives rise to a new type of error that you must guard against ambiguity. Ambiguity errors occur when erasure causes two seemingly distinct generic declarations to resolve to the same erased type, causing a conflict. Here is an example that involves method overloading. 

Now we are good to go with type 1 as shown above to describe that is. method overloading in varargs

Overloading allows different methods to have the same name, but different signatures where the signature can differ by the number of input parameters or type of input parameters, or both. We can overload a method that takes a variable-length argument by following ways:

Case 1: Methods with only Varargs parameters

In this case, Java uses the type difference to determine which overloaded method to call. If one method signature is strictly more specific than the other, then Java chooses it without an error.

Example 

Java




// Java program to illustrate method overloading in varargs
 
// Main class demonstrating varargsDemo
public class GFG {
 
    // Method 1
    // varargs method with float datatype
    static void fun(float... x)
    {
 
        // Print statement
        // whenever this method is called
        System.out.println("float varargs");
    }
 
    // Method 2
    // varargs method with int datatype
    static void fun(int... x)
    {
 
        // Print statement
        // whenever this method is called
        System.out.println("int varargs");
    }
 
    // Method 3
    // varargs method with double datatype
    static void fun(double... x)
    {
 
        // Print statement
        // whenever this method is called
        System.out.println("double varargs");
    }
 
    // Method 4
    // Main driver method
    public static void main(String[] args)
    {
 
        // Calling the above methods
        fun();
    }
}


Output

int varargs

Output Explanation:

This output is due to the fact that int is more specific than double. As specified in the JLS section 15.12.2.5, If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen according to type promotion. The following rules define the direct supertype relation among the primitive types in this case: 

double > float
float  > long
long   > int
int    > char
int    > short
short  > byte

Case 2: Methods with Varargs along with other parameters. In this case, Java uses both the number of arguments and the type of arguments to determine which method to call. 

Example 1:

Java




// Java program to demonstrate Varargs and Overloading
 
// Main class
class GFG {
 
    // Method 1
    // It takes varargs(here integers).
    static void fun(int... a)
    {
 
        // Print statement whenever this method is called
        System.out.print("fun(int ...): "
                         + "Number of args: " + a.length
                         + " Contents: ");
 
        // For each loop is used to
        // display contents
        for (int x : a)
 
            // Print statement
            System.out.print(x + " ");
 
        // New line
        System.out.println();
    }
 
    // Method 2
    // It takes varargs(here booleans).
    static void fun(boolean... a)
    {
 
        // Print statement to display the content
        // whenever this method is called
        System.out.print("fun(boolean ...) "
                         + "Number of args: " + a.length
                         + " Contents: ");
 
        // Iterating using for-each loop to
        // display contents
        for (boolean x : a)
 
            // Print statement to display the content
            // whenever this method is called
            System.out.print(x + " ");
 
        // New line for better readability
        System.out.println();
    }
 
    // Method 3
    // It takes string as a argument
    // followed by varargs(here integers).
    static void fun(String msg, int... a)
    {
 
        // Print statement to display the content
        // whenever this method is called
        System.out.print("fun(String, int ...): " + msg
                         + a.length + " Contents: ");
 
        // Iterating using for-each loop to
        // display contents
        for (int x : a)
 
            System.out.print(x + " ");
 
        // New line for better readability
        System.out.println();
    }
 
    // Method 4
    // Main driver method
    public static void main(String args[])
    {
 
        // Calling the above methods to
        // check for overloaded fun()
        // with different  parameter
 
        // Custom inputs as parameters
 
        fun(1, 2, 3);
        fun("Testing: ", 10, 20);
        fun(true, false, false);
    }
}


Output

fun(int ...): Number of args: 3 Contents: 1 2 3 
fun(String, int ...): Testing: 2 Contents: 10 20 
fun(boolean ...) Number of args: 3 Contents: true false false 

Here the fun() method is overloaded three times.

Note: Sometimes unexpected errors can result when overloading a method that takes a variable-length argument. These errors involve ambiguity because both the methods are valid candidates for invocation. The compiler cannot decide on which method to bind the method call.

Example 2:

Java




// Java program to illustrate Varargs and Ambiguity
 
// Main class
class GFG {
 
    // Method 1
    // It takes varargs(here integers).
    static void fun(int... a)
    {
 
        // Print and display contents
        // whenever this method is called
        System.out.print("fun(int ...): "
                         + "Number of args: " + a.length
                         + " Contents: ");
 
        // Iterating using for-each loop to
        // display contents
        for (int x : a)
 
            // Print statement
            System.out.print(x + " ");
 
        // New line for better readability of output
        System.out.println();
    }
 
    // Method 2
    // It takes varargs(here booleans).
    static void fun(boolean... a)
    {
 
        // Print and display contents
        // whenever this method is called
        System.out.print("fun(boolean ...) "
                         + "Number of args: " + a.length
                         + " Contents: ");
 
        // Iterating using for-each loop to
        // display contents
        for (boolean x : a)
 
            System.out.print(x + " ");
 
        // New line is needed for
        // better readability in output
        System.out.println();
    }
 
    // Method 3
    // Main driver method
    public static void main(String args[])
    {
 
        // Calling overloaded fun() above created
        // with different  parameter
 
        // Custom inputs are passed as parameters
 
        // Case1: ok
        fun(1, 2, 3);
 
        // Case 2: ok
        fun(true, false, false);
 
        // Case 3: Error: Ambiguous!
        fun();
    }
}


Output: 

Output explanation: The overloading of the desired method here named ‘fun()‘ is perfectly correct. However, this program will not compile because of the last call made to ‘fun()’ which can also be interpreted in the code.

Type 2: Ambiguity in multiple inheritances. Inheritance is a relation between two classes where one class inherits the properties of the other class. This relation can be defined using the extends keyword as follows: 

public class A extends B {}

The class which inherits the properties is known as a subclass or, child class and the class whose properties are inherited is a superclass or, parent class. In inheritance, a copy of superclass members is created in the sub-class object. Therefore, using the sub-class object you can access the members of both classes.

There are various types of inheritance available namely single, multilevel, hierarchical, multiple and, hybrid. In multiple inheritances, one class inherits the properties of multiple classes. In other words, in multiple inheritances, we can have one child class and n number of parent classes. Java does not support multiple inheritances (with classes). 

Implementation: 

Diamond problem is one of the major ambiguities that arise here in the case of multiple inheritances. For instance, let us assume that Java does support multiple inheritances. Consider the example below with the following assumptions. Here we have an abstract class named ‘Sample‘ with an abstract method as 

Example

Java




// Java Program to illustrate Diamond Problem Ambiquity
 
// Class 1
// Abstract class
// Parent class
public class abstract Sample {
 
    // Abstract method
    public abstract demo();
}
 
// Then in the same package/folder,
// we have two classes extending this class and
// trying to implement its abstract method, demo().
 
// Class 2
// helper class extending Class 1
public class Super1 extends Sample {
 
    // Method of this base class
    public void demo()
    {
 
        // Print statement whenever this method is called
        System.out.println("demo method of super1");
    }
}
 
// Class 3
// Helper class extending Class 1
public class Super2 extends Sample {
 
    // Method of this base class
    public void demo()
    {
 
        // Print statement whenever this method is called
        System.out.println("demo method of super2");
    }
}
 
// According to our assumption of Java supports multiple
// inheritance we are trying to inherit both classes Super1
// and Super2.
 
// Class 4
// Helper class
// Deriving above two classes: Class2 and Class3
public class SubClass extends Super1, Super2 {
 
    // Method of this class
    // Also, it is main driver method
    public static void main(String args[])
    {
 
        // Creating object
        SubClass obj = new SubClass();
 
        // Trying to access the demo() method
        // with the help of this class object
        obj.demo();
    }
}


Output: 

Output Explanation: Then, as per the basic rule of inheritance, a copy of both demo() methods should be created in the subclass object which leaves the subclass with two methods with the same prototype (name and arguments). Then, if you call the demo() method using the object of the subclass compiler faces an ambiguous situation not knowing which method to call. This issue is known as the diamond problem in Java.

 



Last Updated : 10 Sep, 2021
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads