为什么编译器在静态调用时会接受无效的语法调用(<?>方法)?

Why will the compiler accept an invalid syntax call (<?>method) when statically called?

在方法之前没有 Static/Instance 引用的情况下,是否存在无法调用 Java 泛型方法的原因?喜欢示例代码中的 "case 2" 和 "case 5"。

换句话说,为什么我们可以在没有 static/instance 引用的情况下调用普通方法(如 "case 3"),而在通用方法中我们不能?

public class MyClass {

public static void main(String[] args) {

    MyClass.<String>doWhatEver("Test Me!"); // case 1

    <String>doWhatEver("Test Me2!"); // case 2 COMPILE ERROR HERE

    doSomething("Test Me 3!"); // case 3 (just for compare)

    new MyClass().<String>doMoreStuff("Test me 4"); // case 4

}

public void doX(){
    <String>doMoreStuff("test me 5"); // case 5 COMPILE ERROR HERE
}


public static <T> void doWhatEver(T x){
    System.out.println(x);
}

public static void doSomething(String x){
    System.out.println(x);
}

public <T> void doMoreStuff(T x){
    System.out.println(x);
}

}
  • 您无需为情况 1 和情况 4 指定 <String>,编译器会为您处理。
  • 现在让我们尝试 运行 你的例子,看看会发生什么。

Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - illegal start of expression

就这么简单,你的问题的答案是因为语法无效,javac 规范中不应该这样使用。

不过,这与是否static无关。在构造函数中尝试将静态关键字删除到 doWhatEver 方法:

public MyClass()
{
    <String>doWhatEver("Test Me2!"); //does not compile
    doWhatEver("Test Me2!"); //compile
}

public <T> void doWhatEver(T x){
    System.out.println(x);
}

现在如果你想知道为什么MyClass.<String>doWhat..编译了而<String>doWhat..即使我们修改static关键字也没有编译,让我们看看生成的字节码。

你的行将被编译为:

6: invokestatic  #5                  // Method doWhatEver:(Ljava/lang/Object;)V

哪个更正了你犯的语法错误,但为什么?

尝试编译例如这两行

MyClass.<String>doWhatEver("Test Me2!");
MyClass.doWhatEver("Test Me3!");

然后 运行 javap -v 在 .class 文件上,您会注意到这两个调用被编译为相同的字节码。

4: ldc           #4                  // String Test Me2!
6: invokestatic  #5                  // Method doWhatEver:(Ljava/lang/Object;)V
9: ldc           #6                  // String Test Me3!
11: invokestatic  #5                  // Method doWhatEver:(Ljava/lang/Object;)V

在调用非静态方法的情况下,生成的字节码将是 invokevirtual 而不是:

17: invokevirtual #8                  // Method doWhatEver2:(Ljava/lang/Object;)V

我的猜测是 invokestatic 会直接在常量池(存储静态方法的地方)中搜索指定调用对应的方法并省略类型声明,而 invokevirtual 会在实际 class.

中搜索