2015-03-02 15 views
1

是否有一个原因,Java通用方法不能在方法之前没有静态/实例引用调用?就像示例代码中的“案例2”和“案例5”一样。为什么编译器在静态调用时接受无效的语法调用(<?>方法)?

换句话说,为什么我们可以调用一个没有静态/实例引用的普通方法(比如在“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); 
} 

} 
+2

AFAIK' someMethod()'不是有效的语法。 – 2015-03-02 21:48:19

+0

这是一个语法错误。不过,你不需要'<...>',编译器通常会推断出正确的类型。 – 2015-03-02 21:48:53

+0

@AdrianLeonhard是的,我知道,但不是一个语法错误,它编译和运行,如果你评论情况2和情况5。并且有些情况下,一些API你必须使用这些语法。 – 2015-03-02 21:52:21

回答

4
  • 你并不需要为案件1和4指定<String>,编译器会处理这个给你。
  • 现在让我们试着运行你的例子,看看会发生什么。

异常在线程“主”了java.lang.RuntimeException:不可编译的 源代码 - 表达的非法启动

就这么简单得多,回答你的问题是因为语法无效,这并不意味着在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..编译而如果我们修改了static关键字<String>doWhat..甚至没有编译,让我们来看看生成的字节码。

你的行会被编译成这样:

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将在实际的类中搜索。

+0

非常好的答案,TNX这么多。我刚刚找到了语法规范。规则是,如果是通用方法,则必须使用super()/ this/reference变量/或Class(如果是static)进行调用。 – 2015-03-03 00:42:14

+0

不客气。 – 2015-03-03 00:46:22