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

时间:2015-03-02 21:46:26

标签: java generics methods

是否有理由在方法之前没有静态/实例引用时无法调用Java Generic方法?喜欢&#34;案例2&#34;和&#34;案例5&#34;关于示例代码。

换句话说,为什么我们可以在没有静态/实例引用的情况下调用普通方法(例如在&#34;案例3和#34;中),在通用方法中我们可以调用

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 个答案:

答案 0 :(得分:4)

  • 对于案例1和案例4,您无需指定<String>,编译器将为您处理此问题。
  • 现在让我们尝试运行你的例子,看看会发生什么。
  

线程“main”中的异常java.lang.RuntimeException:Uncompilable   源代码 - 非法开始表达

这很简单,你的问题的答案是因为语法无效,并不意味着在javac规范中以这种方式使用。

但是,这与static无关。在构造函数中尝试将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!");

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

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将搜索实际的班级。