为什么编译器在静态调用时会接受无效的语法调用(<?>方法)?
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.
中搜索
在方法之前没有 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.