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 次。
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.
示例 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被称为菱形问题。