更快地替代Java';s反射

更快地替代Java';s反射,java,performance,reflection,Java,Performance,Reflection,正如我们所知,反射是一种灵活但缓慢的方法,用于在运行时维护和修改代码的行为 但是,如果我们必须使用这样的功能,Java中有没有比反射API更快的用于动态修改的编程技术?这些替代方案相对于反射的优缺点是什么?反射的一个替代方案是动态生成类文件。这个生成的类应该执行所需的操作,例如调用在运行时发现的方法,并实现编译时已知的接口,以便可以使用该接口以非反射方式调用生成的方法。这里有一个陷阱:如果适用,反射在内部也会做同样的事情。这在特殊情况下不起作用,例如,当调用private方法时,因为您无法生成调

正如我们所知,反射是一种灵活但缓慢的方法,用于在运行时维护和修改代码的行为


但是,如果我们必须使用这样的功能,Java中有没有比反射API更快的用于动态修改的编程技术?这些替代方案相对于反射的优缺点是什么?

反射的一个替代方案是动态生成类文件。这个生成的类应该执行所需的操作,例如调用在运行时发现的方法,并实现编译时已知的
接口
,以便可以使用该接口以非反射方式调用生成的方法。这里有一个陷阱:如果适用,反射在内部也会做同样的事情。这在特殊情况下不起作用,例如,当调用
private
方法时,因为您无法生成调用它的合法类文件。因此,在反射实现中有不同类型的调用处理程序,使用生成的代码或本机代码。你不能打败它

但更重要的是,反射会对每次调用进行安全检查。因此,您生成的类将仅在加载和实例化时进行检查,这将是一个巨大的胜利。或者,您可以在
方法
实例上调用
setAccessible(true)
,以关闭安全检查。那么,只剩下自动装箱和varargs数组创建的轻微性能损失

由于Java7两者都有一个替代方案,
MethodHandle
。最大的优势是,与其他两个不同,它甚至可以在安全受限的环境中工作。对
MethodHandle
的访问检查在获取时执行,而不是在调用时执行。它具有所谓的“多态签名”,这意味着您可以使用任意参数类型调用它,而无需自动装箱或创建数组。当然,错误的参数类型将创建适当的
运行时异常

(更新) 使用Java8,可以选择在运行时使用lambda表达式和方法引用语言特性的后端。这个后端正好完成了开头描述的事情,动态生成一个类,该类实现了一个
接口
,当编译时知道它时,您的代码可以直接调用它。确切的机制是特定于实现的,因此没有定义,但是您可以假设实现将尽可能快地进行调用。Oracle的JRE的当前实现非常完美。这不仅使您免于生成这样一个访问器类的负担,而且还能够完成您从未做过的事情—通过生成的代码调用甚至
private
方法。我已经更新了示例以包含此解决方案。本例使用了一个标准的
接口
,该接口已经存在,并且恰好具有所需的方法签名。如果不存在此类匹配的
接口
,则必须使用具有正确签名的方法创建自己的访问器功能接口。但是,当然,现在的示例代码需要运行Java8

下面是一个简单的基准测试示例:

import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.function.IntBinaryOperator;

public class TestMethodPerf
{
  private static final int ITERATIONS = 50_000_000;
  private static final int WARM_UP = 10;

  public static void main(String... args) throws Throwable
  {
 // hold result to prevent too much optimizations
    final int[] dummy=new int[4];

    Method reflected=TestMethodPerf.class
      .getDeclaredMethod("myMethod", int.class, int.class);
    final MethodHandles.Lookup lookup = MethodHandles.lookup();
    MethodHandle mh=lookup.unreflect(reflected);
    IntBinaryOperator lambda=(IntBinaryOperator)LambdaMetafactory.metafactory(
      lookup, "applyAsInt", MethodType.methodType(IntBinaryOperator.class),
      mh.type(), mh, mh.type()).getTarget().invokeExact();

    for(int i=0; i<WARM_UP; i++)
    {
      dummy[0]+=testDirect(dummy[0]);
      dummy[1]+=testLambda(dummy[1], lambda);
      dummy[2]+=testMH(dummy[1], mh);
      dummy[3]+=testReflection(dummy[2], reflected);
    }
    long t0=System.nanoTime();
    dummy[0]+=testDirect(dummy[0]);
    long t1=System.nanoTime();
    dummy[1]+=testLambda(dummy[1], lambda);
    long t2=System.nanoTime();
    dummy[2]+=testMH(dummy[1], mh);
    long t3=System.nanoTime();
    dummy[3]+=testReflection(dummy[2], reflected);
    long t4=System.nanoTime();
    System.out.printf("direct: %.2fs, lambda: %.2fs, mh: %.2fs, reflection: %.2fs%n",
      (t1-t0)*1e-9, (t2-t1)*1e-9, (t3-t2)*1e-9, (t4-t3)*1e-9);

    // do something with the results
    if(dummy[0]!=dummy[1] || dummy[0]!=dummy[2] || dummy[0]!=dummy[3])
      throw new AssertionError();
  }

  private static int testMH(int v, MethodHandle mh) throws Throwable
  {
    for(int i=0; i<ITERATIONS; i++)
      v+=(int)mh.invokeExact(1000, v);
    return v;
  }

  private static int testReflection(int v, Method mh) throws Throwable
  {
    for(int i=0; i<ITERATIONS; i++)
      v+=(int)mh.invoke(null, 1000, v);
    return v;
  }

  private static int testDirect(int v)
  {
    for(int i=0; i<ITERATIONS; i++)
      v+=myMethod(1000, v);
    return v;
  }

  private static int testLambda(int v, IntBinaryOperator accessor)
  {
    for(int i=0; i<ITERATIONS; i++)
      v+=accessor.applyAsInt(1000, v);
    return v;
  }

  private static int myMethod(int a, int b)
  {
    return a<b? a: b;
  }
}
导入java.lang.invoke.LambdaMetafactory;
导入java.lang.invoke.MethodHandle;
导入java.lang.invoke.MethodHandles;
导入java.lang.invoke.MethodType;
导入java.lang.reflect.Method;
导入java.util.function.IntBinaryOperator;
公共类TestMethodPerf
{
私有静态最终整数迭代=50_000_000;
专用静态最终int预热=10;
publicstaticvoidmain(字符串…参数)抛出Throwable
{
//保留结果以防止过多优化
最终整数[]虚拟=新整数[4];
反射的方法=TestMethodPerf.class
.getDeclaredMethod(“myMethod”,int.class,int.class);
final MethodHandles.Lookup Lookup=MethodHandles.Lookup();
MethodHandle mh=查找。未反射(反射);
IntBinaryOperator lambda=(IntBinaryOperator)LambdaMetafactory.metafactory(
查找“applyAsInt”、MethodType.MethodType(IntBinaryOperator.class),
mh.type(),mh,mh.type()).getTarget().invokeExact();

对于(inti=0;i反射的替代方法是使用接口

我们可以从反思中获得许多好处,但却很少受到影响 对许多人来说,仅仅以非常有限的形式使用它就降低了成本 必须使用编译时不可用的类的程序, 编译时存在一个适当的接口或超类 引用类。如果是这种情况,您可以创建 实例,并通过其接口或 如果适当的构造函数没有参数,那么 甚至不需要使用java.lang.reflect;Class.newInstance方法 提供所需的功能

使用反射仅用于创建对象,即

// Reflective instantiation with interface access
   public static void main(String[] args) {
       // Translate the class name into a Class object
       Class<?> cl = null;
       try {
           cl = Class.forName(args[0]);
       } catch(ClassNotFoundException e) {
           System.err.println("Class not found.");
           System.exit(1);
       }
       // Instantiate the class
       Set<String> s = null;
       try {
           s = (Set<String>) cl.newInstance();
       } catch(IllegalAccessException e) {
           System.err.println("Class not accessible.");
           System.exit(1);
       } catch(InstantiationException e) {
           System.err.println("Class not instantiable.");
           System.exit(1);
       }
       // Exercise the set
       s.addAll(Arrays.asList(args).subList(1, args.length));
       System.out.println(s);
}
//具有接口访问权限的反射实例化
公共静态void main(字符串[]args){
//将类名转换为类对象
类别cl=空;
试一试{
cl=类forName(args[0]);
}catch(classnotfounde异常){
System.err.println(“未找到类”);
系统出口(1);
}
//实例化该类
设置s=null;
试一试{
s=(Set)cl.newInstance();
}捕获(非法访问例外e){
System.err.println(“类不可访问”);
系统出口(1);
}捕获(实例化异常e){
System.err.println(“类不可实例化”);
系统出口(1);
}
//练习布景
s、 addAll(Arrays.asList(args.subList)(1,args.length));
系统输出打印项次;
}
虽然这个程序只是一个玩具,但它展示的技术是 非常强大。玩具程序可以很容易地转换成
private static String myStaticMethod(int a, Integer b){ /*some logic*/ }
private float myInstanceMethod(String a, Boolean b){ /*some logic*/ }
Method method = MyClass.class.getDeclaredMethod("myStaticMethod", int.class, Integer.class); //Regular reflection call
Lambda lambda = LambdaFactory.create(method);  
String result = (String) lambda.invoke_for_Object(1000, (Integer) 565); //Don't rely on auto boxing of arguments!

Method method = MyClass.class.getDeclaredMethod("myInstanceMethod", String.class, Boolean.class);
Lambda lambda = LambdaFactory.create(method);
float result = lambda.invoke_for_float(new MyClass(), "Hello", (Boolean) null);  //No need to cast primitive results!