这取决于您希望如何调用这些方法。如果您希望从其他Java源代码调用这些方法,那么由于Edwin的答案中所示的原因,它被认为是无效的。这是 Java 语言的一个限制。
但是,并非所有类都需要从Java源代码生成(考虑所有使用JVM作为其运行时的语言:JRuby,Jython等)。在字节码级别,JVM 可以消除这两种方法的歧义,因为字节码指令指定了它们期望的返回类型。例如,下面是一个用 Jasmin 编写的类,可以调用以下任一方法:
.class public CallAmbiguousMethod
.super java/lang/Object
.method public static main([Ljava/lang/String;)V
.limit stack 3
.limit locals 1
; Call the method that returns String
aconst_null
invokestatic TestWillThatCompile/f(Ljava/util/List;)Ljava/lang/String;
; Call the method that returns Integer
aconst_null
invokestatic TestWillThatCompile/f(Ljava/util/List;)Ljava/lang/Integer;
return
.end method
我使用以下命令将其编译为类文件:
java -jar jasmin.jar CallAmbiguousMethod.j
并使用以下方式调用它:
java CallAmbiguousMethod
看哪,输出是:
> java CallAmbiguousMethod
strings
numbers
更新
Simon 发布了一个调用这些方法的示例程序:
import java.util.Arrays;
import java.util.List;
class RealyCompilesAndRunsFine {
public static String f(List<String> list) {
return list.get(0);
}
public static Integer f(List<Integer> list) {
return list.get(0);
}
public static void main(String[] args) {
final String string = f(Arrays.asList("asdf"));
final Integer integer = f(Arrays.asList(123));
System.out.println(string);
System.out.println(integer);
}
}
以下是生成的 Java 字节码:
>javap -c RealyCompilesAndRunsFine
Compiled from "RealyCompilesAndRunsFine.java"
class RealyCompilesAndRunsFine extends java.lang.Object{
RealyCompilesAndRunsFine();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."":()V
4: return
public static java.lang.String f(java.util.List);
Code:
0: aload_0
1: iconst_0
2: invokeinterface #2, 2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
7: checkcast #3; //class java/lang/String
10: areturn
public static java.lang.Integer f(java.util.List);
Code:
0: aload_0
1: iconst_0
2: invokeinterface #2, 2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
7: checkcast #4; //class java/lang/Integer
10: areturn
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: anewarray #3; //class java/lang/String
4: dup
5: iconst_0
6: ldc #5; //String asdf
8: aastore
9: invokestatic #6; //Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
12: invokestatic #7; //Method f:(Ljava/util/List;)Ljava/lang/String;
15: astore_1
16: iconst_1
17: anewarray #4; //class java/lang/Integer
20: dup
21: iconst_0
22: bipush 123
24: invokestatic #8; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
27: aastore
28: invokestatic #6; //Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
31: invokestatic #9; //Method f:(Ljava/util/List;)Ljava/lang/Integer;
34: astore_2
35: getstatic #10; //Field java/lang/System.out:Ljava/io/PrintStream;
38: aload_1
39: invokevirtual #11; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
42: getstatic #10; //Field java/lang/System.out:Ljava/io/PrintStream;
45: aload_2
46: invokevirtual #12; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
49: return
事实证明,Sun 编译器正在生成消除方法歧义所需的字节码(请参阅最后一种方法中的指令 12 和 31)。
更新 #2
Java语言规范表明,这实际上可能是有效的Java源代码。在第 449 页(§15.12 方法调用表达式),我们看到以下内容:
可能没有一种方法是最具体的,因为有两种或两种以上方法具有最大的特异性。在这种情况下:
- 如果所有最大特定方法都具有等效覆盖 (§8.4.2) 签名,则:
- 如果恰好有一个最大特异性的方法没有被声明为抽象,那么它就是最具体的方法。
- 否则,如果所有最大特定方法都声明为抽象,并且所有最大特定方法的签名具有相同的擦除 (§4.6),则在具有最具体返回类型的最大特定方法的子集中任意选择最具体的方法。但是,当且仅当在每个最大特定方法的 throws 子句中声明了该异常或其擦除时,才考虑引发已检查异常的最具体方法。
- 否则,我们说方法调用是不明确的,并且发生了编译时错误。
除非我弄错了,否则此行为应该仅适用于声明为抽象的方法,但是......
更新 #3
感谢ILMTitan的评论:
@Adam Paynter:你的粗体文本并不重要,因为这只是两种方法覆盖等价的情况,Dan表明情况并非如此。因此,决定因素必须是JLS在确定最具体的方法时是否考虑了泛型类型。– ILMTitan