答复: 通过代码简单介绍JDK 7的MethodHandle,并与.NET的委托对比(二)

原帖地址: 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文件我也放在附件里了,不相信例子的输出结果的话请自己执行一下,眼见为实。

你可能感兴趣的:(java,jvm,jdk,.net,rubygems)