原帖地址:
http://www.iteye.com/topic/477934?page=3#1186622
再顺手转回来免得以后找起来麻烦
star022 写道
Java代码
findStatic(
TestMethodHandle1.class, // 方法所属类型(Class)
"hello", // 方法名
type // 由参数和返回值类型组成的“方法类型”
);
type// 由参数和返回值类型组成的“方法类型”
“方法类型”这个参数设计得很失败,
一个类中的方法,如果方法名和参数个数及类型一样,这个类能正确编译吗?
其实MethodHandle最终只需要暴露类似这样一个静态方法即可:
//方法如果没有返回值,为void
MethodHandle.<T>invoke(
Class clazz, // 方法所属类型(Class)
String methodName, // 方法名
Object... params//方法参数,可以运行时确定参数个数及类型,定位到具体方法
)
你的思路被局限在Java“语言”里了。JSR 292的主要服务对象是JVM上的动态语言,而不是Java。如果你了解JVM的spec而不只是Java的spec,你应该能理解Java字节码不是只能通过Java编译器来生成的。你可以把我之前回帖的那段完整引用一次:
RednaxelaFX 写道
star022 写道
定位到一个java方法,其实只需要类型(Class),方法名及参数即可。
对,说得一点也没错,所以MethodHandles的API就是这样的:
引用
findStatic(
TestMethodHandle1.class, // 方法所属类型(Class)
"hello", // 方法名
type // 由参数和返回值类型组成的“方法类型”
);
如果只是要做Java的method overload resolution,当然只要参数类型不要返回值类型就够了,但了解class文件及JVM内部数据组织方式的话就会知道,方法的签名(signature)在class文件里是以方法描述符(method descriptor)的形式存在,而该描述符上是有返回值类型的。MethodHandles的API这么设计就是为了快,能更直接的访问VM里的信息,以最快的方式找到目标方法。
如果那段文字仍然不能让你明白,那请看下面的例子。
首先要明确的是,在Java语言里,method overload只依赖于方法名和参数类型,不考虑返回值类型;仅在返回值类型不同的方法无法通过
Java编译器的编译。
但生成Java字节码的方式有很多:JVM上有非常多其它语言,它们的编译器都可以生成Java字节码;动态代理要生成字节码;再不行,手工生成字节码也是可以的。从JVM的角度看,无论字节码的来源是什么,只要符合class文件规范、只要加载成功,JVM就可以执行那些字节码。
这里我用bitescript来生成一个class文件,类名为TestMethodSameName,包括两个foo方法,它们只在返回值类型上不同:
require 'rubygems'
require 'bitescript'
include BiteScript
fb = FileBuilder.build(__FILE__) do
public_class 'TestMethodSameName' do
public_static_method 'foo', void, int do
ldc 'TestMethodSameName.foo:(I)V'
aprintln
returnvoid
end
public_static_method 'foo', int, int do
ldc 'TestMethodSameName.foo:(I)I'
aprintln
iload 0
ireturn
end
public_static_method 'main', void, string[] do
push_int 123
invokestatic this, 'foo', [void, int]
push_int 456
invokestatic this, 'foo', [int, int]
pop
returnvoid
end
end
end
fb.generate do |filename, class_builder|
File.open(filename, 'w') do |file|
file.write(class_builder.generate)
end
end
得到的class文件,内容如下:
Compiled from "test5.rb"
public class TestMethodSameName extends java.lang.Object{
public static void foo(int);
Code:
0: ldc #9; //String TestMethodSameName.foo:(I)V
2: getstatic #15; //Field java/lang/System.out:Ljava/io/PrintStream;
5: swap
6: invokevirtual #21; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
9: return
public static int foo(int);
Code:
0: ldc #24; //String TestMethodSameName.foo:(I)I
2: getstatic #15; //Field java/lang/System.out:Ljava/io/PrintStream;
5: swap
6: invokevirtual #21; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
9: iload_0
10: ireturn
public static void main(java.lang.String[]);
Code:
0: bipush 123
2: invokestatic #28; //Method foo:(I)V
5: sipush 456
8: invokestatic #30; //Method foo:(I)I
11: pop
12: return
}
如果用Java语法来写,就是:
public class TestMethodSameName {
public static void foo(int i) {
System.out.println("TestMethodSameName.foo:(I)V");
}
public static int foo(int i) {
System.out.println("TestMethodSameName.foo:(I)I");
return i;
}
public static void main(String[] args) {
foo(123); // foo:(I)V
foo(456); // foo:(I)I
}
}
再次注意到这段代码用
Java编译器确实编译不了。但上面生成的字节码对JVM来说却是完全没问题的。执行结果输出如下:
引用
TestMethodSameName.foo:(I)V
TestMethodSameName.foo:(I)I
这很好的说明了在深入到底层去挖掘MethodHandle时,指定返回值类型的必要性。
JDK原本包含的普通反射API之所以不需要指定返回值类型是因为它只是为Java语言服务的。如今的JSR 292则是为JVM上所有语言服务,主要目标是各种动态语言,但也不拒绝Java去使用它。
关于bitescript的用法,请参考
这一帖的例子。上面生成的class文件我也放在附件里了,不相信例子的输出结果的话请自己执行一下,眼见为实。