Javassist-官方文档中文翻译-第6-10章-泛型-可变参数-J2ME-装箱拆箱-Debug

- 日理万妓 2023-10-10 08:22 88阅读 0赞

这是把官方的文档给翻译了,顺便学习,一共10章,可以到下面地址查看,里面可能有翻译不准的地方,欢迎指正

  1. https://github.com/IndustriousSnail/javassist-learn

文章目录

    • 六、泛型
    • 七、可变参数(int… args)
    • 八、J2ME
    • 九、拆箱和装箱
    • 十、Debug

六、泛型

Javassist的底层API完全支持了Java5中的泛型。另一方面,顶层API,例如 CtClass, 不能直接支持泛型。然而,这个对于字节码转换不是一个严重的问题。

Java中的泛型是通过消除技术实现的。 在编译之后,所有的类型参数都将消失。例如,假定你的源码声明了一个参数化的类型 Vector<String :

  1. Vector<String> v = new Vector<String>();
  2. :
  3. String s = v.get(0);

编译后的字节码就等同于下面:

  1. Vector v = new Vector();
  2. :
  3. String s = (String)v.get(0);

所以当你写一个字节码转换器时,你可以删除所有的类型参数。因为被嵌在Javassist中的编译器不支持泛型,所以对于使用Javassis插入的代码,你必须插入一个显式的类型转换。例如通过 CtMethod.make() 插入的代码。如果源码是被正常的Java编译器编译的话,比如javac,你就不需要做类型转换了。

例如,如果你有这么一个类:

  1. public class Wrapper<T> {
  2. T value;
  3. public Wrapper(T t) { value = t; }
  4. }

你想增添一个接口 Getter 到类 Wrapper 中:

  1. public interface Getter<T> {
  2. T get();
  3. }

那么你真正增添的是 Getter (类型参数被丢弃了),并且你必须向 Wrapper 类增添的方法就是下面这样一个简单的方法:

  1. public Object get() { return value; }

注意,不需要类型参数。因为 get 返回的是 Object ,所以在调用方需要显示的增加类型转换。例如,如果类型参数TString, 那么 (String) 必须像下面这样被插入:

  1. Wrapper w = ...
  2. String s = (String)w.get();

如果编译器是正常的Java编译器,那么不需要显式的指定类型转换,它会自动插入类型转换代码。

七、可变参数(int… args)

目前,Javassist不直接支持可变参数。所以要让一个方法拥有可变参数,你必须显式的设置方法修饰符。但是这是容易的。假定现在你想创建下面的这个方法:

  1. public int length(int... args) { return args.length; }

上面的代码使用Javassist可以这样创建:

  1. CtClass cc = /* target class */;
  2. CtMethod m = CtMethod.make("public int length(int[] args) { return args.length; }", cc);
  3. m.setModifiers(m.getModifiers() | Modifier.VARARGS);
  4. cc.addMethod(m);

参数类型 int… 被变成了 int[] , 并且 Modifier.VARARGS 被增添到了方法修饰符中。

要在Javassist中的源码文本中调用该方法,你必须这样写:

  1. length(new int[] { 1, 2, 3 });

不能使用Java原生的调用方式:

  1. length(1, 2, 3);

八、J2ME

如果你要修改的文件是J2ME环境的,那么你必须执行预校验。预校验是生成堆栈映射(stack map)的基础,它与JDK1.6中的堆栈映射表很像。只有javassist.bytecode.MethodInfo.doPreverify为true的时候,Javassist才会为J2ME维护堆栈映射。

你也可以手工的为修改的方法生成一个堆栈映射。比如下面这个,m 是一个 CtMethod 对象,你可以调用下面方法来生成一个堆栈映射:

  1. m.getMethodInfo().rebuildStackMapForME(cpool);

这里, cpool 是一个 ClassPool 对象, 可以通过调用CtClassgetClassPool() 方法获取。ClassPool 对象负责从给定路径找到class文件,这个前面章节已经说过了。要获取所有的CtMethod对象,可以调用CtClassgetDeclaredMethods方法。

九、拆箱和装箱

Java中的拆箱和装箱是个语法糖。是没有字节码的。所以Javassist的编译器不支持它们。例如,下面这个语句在Java中是合法的:

  1. Integer i = 3;

因为装箱是暗中执行。 对于Javassist来说,然而,你必须显式的将int转换为Integer

  1. Integer i = new Integer(3);

十、Debug

CtClass.debugDump 的值设置成一个目录,那么Javassist修改和生成的所有class文件都将会被保存在该目录下。要是不想弄,把 CtClass.debugDump 设置为null就行了。默认值也是null。

例如:

  1. CtClass.debugDump = "./dump";

Javassist修改的所有class文件都将存储在 ./dump 目录下。

发表评论

表情:
评论列表 (有 0 条评论,88人围观)

还没有评论,来说两句吧...

相关阅读

    相关 C#委托 事件 装箱

    一、委托 我感觉的委托就是指一个函数指针,指向被委托的方法,在定义的时候把返回值类型与参数全部定义好,创建的实例相当于一个指向被委托函数的指针,然后将需要的数据通过委托传进