这取决于您希望如何调用这些方法。如果您希望从其他 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
字符串
数字
更新
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
编译自“RealyCompilesAndRunsFine.java”
类 RealyCompilesAndRunsFine 扩展 java.lang.Object{
RealyCompilesAndRunsFine();
代码:
0:aload_0
1:调用特殊#1;//方法 java/lang/Object."":()V
4:返回
公共静态 java.lang.String f(java.util.List);
代码:
0:aload_0
1:iconst_0
2:调用接口#2、2;//接口方法 java/util/List.get:(I)Ljava/lang/Object;
7:检查广播#3;//类java/lang/String
10:返回
公共静态 java.lang.Integer f(java.util.List);
代码:
0:aload_0
1:iconst_0
2:调用接口#2、2;//接口方法 java/util/List.get:(I)Ljava/lang/Object;
7:检查广播#4;//类 java/lang/Integer
10:返回
公共静态无效主(java.lang.String[]);
代码:
0:iconst_1
1:新数组#3;//类java/lang/String
4:重复
5:iconst_0
6:最不发达国家#5;//字符串asdf
8:商店
9:调用静态#6;//方法 java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
12:调用静态#7;//方法f:(Ljava/util/List;)Ljava/lang/String;
15:astore_1
16:图标st_1
17:新阵列#4;//类 java/lang/Integer
20:重复
21:iconst_0
22:双推 123
24:调用静态#8;//方法 java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
27:商店
28:调用静态#6;//方法 java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
31:调用静态#9;//方法f:(Ljava/util/List;)Ljava/lang/Integer;
34:astore_2
35:获取静态#10;//字段 java/lang/System.out:Ljava/io/PrintStream;
38:加载_1
39:调用虚拟#11;//方法java/io/PrintStream.println:(Ljava/lang/String;)V
42:获取静态#10;//字段 java/lang/System.out:Ljava/io/PrintStream;
45:加载_2
46:调用虚拟#12;//方法java/io/PrintStream.println:(Ljava/lang/Object;)V
49:返回
事实证明,Sun 编译器正在生成消除方法歧义所需的字节码(参见最后一个方法中的说明 12 和 31)。
更新#2
Java 语言规范表明这实际上可能是有效的 Java 源代码。在第 449 页(§15.12 方法调用表达式)我们看到:
可能没有一种方法是最具体的,因为有两种或多种方法是最具体的。在这种情况下:
- 如果所有最具体的方法都具有覆盖等效(第 8.4.2 节)签名,则:
- 如果没有将最具体的方法之一声明为抽象的,则它是最具体的方法。
- 否则,如果所有最大特定方法都被声明为抽象方法,并且所有最大特定方法的签名具有相同的擦除(第 4.6 节),则在具有最具体的返回类型。但是,当且仅当在每个最大特定方法的 throws 子句中声明了该异常或其擦除时,才认为最特定的方法会引发已检查异常。
- 否则,我们说方法调用有歧义,出现编译时错误。
除非我弄错了,否则这种行为应该只适用于声明为抽象的方法,虽然......
更新#3
感谢 ILMTitan 的评论:
@Adam Paynter:您的粗体文本无关紧要,因为这只是两种方法等效覆盖的情况,而丹表明情况并非如此。因此,决定因素必须是 JLS 在确定最具体的方法时是否考虑泛型类型。– ILM钛