📜  Java的歧义

📅  最后修改于: 2022-05-13 01:54:51.172000             🧑  作者: Mango

Java的歧义

歧义是那些在Java语言规范中没有明确定义的问题。不同编译器在几个示例程序上产生的不同结果支持我们的观察。在这里,我们将按以下顺序进行讨论。

  • 方法重载中的歧义方法
    • 只有 Varargs 参数的方法
    • 带有可变参数和其他参数的方法
  • 多重继承中的歧义
    • 钻石问题

类型一:方法重载中的歧义方法

当您重载方法时,您可能会产生一种模棱两可的情况,即编译器无法确定使用哪种方法。例如,考虑以下重载的computeBalance()方法声明:

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

如果您声明了一个名为 myDeposit 的双变量并进行了诸如 computeBalance(myDeposit); 之类的方法调用,那么您将创建一个模棱两可的情况。这两种方法都与您的呼叫完全匹配。您可能会争辩说,使用名为 myDeposit 的变量的调用“似乎”应该转到具有名为 deposit 的参数的方法的版本,但Java不会基于变量名做出任何假设。每个版本的 computeBalance() 都可以接受一个双精度值, Java不假定您打算使用哪个版本。

歧义错误



包含泛型会产生一种新型错误,您必须防止歧义。当擦除导致两个看似不同的泛型声明解析为相同的擦除类型时,就会发生歧义错误,从而导致冲突。这是一个涉及方法重载的示例。

现在我们可以使用上面显示的类型 1 来描述它。可变参数中的方法重载

重载允许不同的方法具有相同的名称,但具有不同的签名,其中签名可能因输入参数的数量或输入参数的类型或两者而异。我们可以通过以下方式重载接受可变长度参数的方法:

案例 1:只有 Varargs 参数的方法

在这种情况下, Java使用类型差异来确定要调用哪个重载方法。如果一个方法签名严格来说比另一个更具体,那么Java毫无错误地选择它。

例子

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();
    }
}


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);
    }
}


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();
    }
}


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();
    }
}


输出
int varargs

输出说明:



这个输出是因为 int 比 double 更具体。如 JLS 部分 15.12.2.5 中所述,如果多个成员方法既可访问又适用于方法调用,则必须选择一个成员方法来为运行时方法分派提供描述符。 Java编程语言使用根据类型提升选择最具体的方法的规则。在这种情况下,以下规则定义了原始类型之间的直接超类型关系:

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

案例 2:带有 Varargs 和其他参数的方法。在这种情况下, Java使用参数的数量和参数的类型来确定调用哪个方法。

示例 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);
    }
}
输出
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 

这里的 fun() 方法重载了 3 次。

示例 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();
    }
}

输出:



输出说明:此处名为“ fun() ”的所需方法的重载是完全正确的。但是,由于最后一次调用“fun()” ,这也可以在代码中进行解释,因此该程序将无法编译。

类型 2:多重继承中的歧义。继承是两个类之间的关系,其中一个类继承了另一个类的属性。这种关系可以使用 extends 关键字定义如下:

public class A extends B {}

继承属性的类称为子类或子类,继承其属性的类称为超类或父类。在继承中,在子类对象中创建了超类成员的副本。因此,使用子类对象可以访问两个类的成员。

有多种类型的继承可用,即单、多级、分层、多重和混合。在多重继承中,一个类继承了多个类的属性。换句话说,在多重继承中,我们可以有一个子类和 n 个父类。 Java不支持多重继承(使用类)。

执行:

在多重继承的情况下,钻石问题是这里出现的主要歧义之一。例如,让我们假设Java确实支持多重继承。考虑以下具有以下假设的示例。这里我们有一个名为“ Sample ”的抽象类,其抽象方法为

例子

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();
    }
}

输出:

输出说明:然后,根据继承的基本规则,应该在子类对象中创建两个 demo() 方法的副本,这使子类具有两个具有相同原型(名称和参数)的方法。那么,如果使用子类的对象调用demo()方法,编译器就会面临不知道调用哪个方法的模棱两可的情况。这个问题在Java被称为菱形问题。