如何为Fields创建function接口实现?

考虑Animal类中的字段weight 。 我希望能够创建一个gettersetterfunction接口对象来操作这个字段。

 class Animal { int weight; } 

我目前的方法类似于用于方法的方法:

 public static Supplier getter(Object obj, Class cls, Field f) throws Exception { boolean isstatic = Modifier.isStatic(f.getModifiers()); MethodType sSig = MethodType.methodType(f.getType()); Class dCls = Supplier.class; MethodType dSig = MethodType.methodType(Object.class); String dMthd = "get"; MethodType dType = isstatic? MethodType.methodType(dCls) : MethodType.methodType(dCls, cls); MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle fctry = LambdaMetafactory.metafactory(lookup, dMthd, dType, dSig, lookup.unreflectGetter(f), sSig).getTarget(); fctry = !isstatic && obj!=null? fctry.bindTo(obj) : fctry; return (Supplier)fctry.invoke(); } 

但是这会产生以下错误:

 java.lang.invoke.LambdaConversionException: Unsupported MethodHandle kind: getField x.Animal.weight:()int 

UPDATE

我正在尝试创建一个实现interface Map ObjectMap类,它基本上试图将对象 表示Map ,其中对象可以是任何类型。 当前正在使用Field.set() Field.get()Field.set()来操作get()put()方法中的字段,并使用上面提到的方法来创建用于调用gettersetter方法的SupplierConsumer对象。 我想知道我是否可以将两种不同的方法合并为一种。

可以通过ObjectMap用作Map示例类:

 public class ThisCanBeAnything { /* fields */ public String normalField; private int hiddenFiled; private String hiddenReadonlyField; /* getters and setters */ public int hiddenField() { return hiddenField; } public void hiddenField(int v) { System.out.println("set: hiddenField="+v); hiddenField = v; } public String hiddenReadonlyField() { return hiddenReadonlyField; } } 

这是预期的用法:

 Object o = new ThisCanBeAnything(); Map m = new ObjectMap(o); m.put("normalField", "Normal"); System.out.println(m.get("normalField")); // Normal m.put("hiddenField", 1); // set: hiddenField=1 System.out.println(m.get("hiddenField")); // 1 m.put("hiddenReadonlyField", 1); // does not do anything System.out.println(m.get("hiddenReadonlyField")); // null 

您可以直接编写lambda,根本不需要LambdaMetafactory

 public static Supplier getter(Object obj, Field f) { return () -> { try { return f.get(obj); } catch (IllegalAccessException e) { throw new RuntimeException(e); } }; } 

或运行时类型安全版本:

 public static  Supplier getter(Object obj, Class fieldClass, Field f) { if (!fieldClass.isAssignableFrom(f.getType())) throw new RuntimeException("Field is not of expected type"); return () -> { try { return (T) f.get(obj); } catch (IllegalAccessException e) { throw new RuntimeException(e); } }; } 

例如:

 private class X { public int a; } @Test public void supplier_getter_test() throws NoSuchFieldException { X a = new X(); aa = 5; Supplier sup = getter(a, int.class, X.class.getField("a")); assertEquals(5, sup.get().intValue()); } 

你需要做的太难了。 如果有Field ,可以直接在查找工厂上调用MethodHandle来检索MethodHandle

生成一个方法句柄,提供对reflection字段的读访问权限。 方法句柄的类型将具有字段值类型的返回类型。 如果该字段是静态的,则方法句柄不带参数。 否则,它的单个参数将是包含该字段的实例。

 public static Supplier getter(Object obj, Class cls, Field f) { f.setAccessible(true); MethodHandles.Lookup lookup = MethodHandles.lookup(); return () -> { try { MethodHandle handle = lookup.unreflectGetter(f); return Modifier.isStatic(f.getModifiers()) ? handle.invoke() : handle.invoke(obj); } catch (Throwable t) { throw new IllegalArgumentException(t); } }; } 

这将返回该字段值的供应商。 根据字段的可访问性,您可能需要调用setAccessible(true)

请注意,方法句柄和reflectionAPI 在性能方面也有所不同,可能会更快。

您不能MethodHandle字段直接访问的MethodHandle绑定到函数接口实例,但您可以绑定Field实例的访问器方法:

 public static Supplier getter(Object obj, Class cls, Field f) throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle get=lookup.findVirtual(Field.class,"get",MethodType.genericMethodType(1)); MethodHandle fctry = LambdaMetafactory.metafactory(lookup, "get", get.type().changeReturnType(Supplier.class), MethodType.genericMethodType(0), get, MethodType.genericMethodType(0)).getTarget(); return (Supplier)fctry.invoke(f, Modifier.isStatic(f.getModifiers())? null: obj); } 

虽然在此特定示例中您可以考虑生成IntSupplier

 public static IntSupplier getter(Object obj, Class cls, Field f) throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle get=lookup.findVirtual(Field.class, "getInt", MethodType.methodType(int.class, Object.class)); MethodHandle fctry = LambdaMetafactory.metafactory(lookup, "getAsInt", get.type().changeReturnType(IntSupplier.class), MethodType.methodType(int.class), get, MethodType.methodType(int.class)).getTarget(); return (IntSupplier)fctry.invoke(f, Modifier.isStatic(f.getModifiers())? null: obj); } 

 final Animal animal = new Animal(); IntSupplier s=getter(animal, Animal.class, Animal.class.getDeclaredField("weight")); animal.weight=42; System.out.println(s.getAsInt()); 

function风格让您以新的方式思考这些事情。 而不是像基于reflection的方法

 Supplier getter(Object obj, Class cls, Field f){...} 

尝试类似的东西

 static  Supplier getter(O obj, Function extractor) { return () -> extractor.apply(obj); } 

你会调用它

 Supplier getWeight = getter(animal, a -> a.weight); Integer weight = getWeight.get(); 

a -> a.weight比通过reflection提出Field更难吗?

一个优点是您可以根据需要使用字段或方法,例如,如果您添加了重量的吸气剂,

 Supplier getWeight = getter(animal, Animal::getWeight); 

类似的二传手工厂可能是

 static  Consumer setter(O obj, BiConsumer modifier) { return field -> modifier.accept(obj,field); } 

像这样调用

 Consumer setWeight = setter(animal, (a, w) -> a.weight = w); setWeight.accept(90); 

我知道这是一个迟到的答案,但我已经开发了一个库,您可以使用MethodHandle任何MethodHandle转换为lambda函数。 性能与手动实现具有直接访问function的function相同。

impl基于以下事实:静态最终MethodHandle被内联到与直接访问一样快的程度。 有关这方面的更多信息可以在这里找到: 如何提高Field.set的性能(使用MethodHandles进行perhap)?

该库可以在这里找到: https : //github.com/LanternPowered/Lmbda 。 现在您将不得不使用Jitpack来访问它(小型库,因此编译时间不会太长): https ://jitpack.io/#LanternPowered/Lmbda

在对象上设置字段的示例:

 import org.lanternpowered.lmbda.LmbdaFactory; import org.lanternpowered.lmbda.LmbdaType; import org.lanternpowered.lmbda.MethodHandlesX; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.util.function.ObjIntConsumer; public class LambdaSetterTest { public static void main(String... args) throws Exception { final MethodHandles.Lookup lookup = MethodHandlesX.privateLookupIn(TestObject.class, MethodHandles.lookup()); final MethodHandle methodHandle = lookup.findSetter(TestObject.class, "data", int.class); final ObjIntConsumer setter = LmbdaFactory.create(new LmbdaType>() {}, methodHandle); final TestObject object = new TestObject(); System.out.println(100 == object.getData()); setter.accept(object, 10000); System.out.println(10000 == object.getData()); } public static class TestObject { private int data = 100; int getData() { return this.data; } } } 

从对象获取字段:

 import org.lanternpowered.lmbda.LmbdaFactory; import org.lanternpowered.lmbda.LmbdaType; import org.lanternpowered.lmbda.MethodHandlesX; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.util.function.ToIntFunction; public class LambdaSetterTest { public static void main(String... args) throws Exception { final MethodHandles.Lookup lookup = MethodHandlesX.privateLookupIn(TestObject.class, MethodHandles.lookup()); final MethodHandle methodHandle = lookup.findGetter(TestObject.class, "data", int.class); final ToIntFunction getter = LmbdaFactory.create(new LmbdaType>() {}, methodHandle); final TestObject object = new TestObject(); System.out.println(100 == getter.applyAsInt(object)); object.setData(10000); System.out.println(10000 == getter.applyAsInt(object)); } public static class TestObject { private int data = 100; void setData(int value) { this.data = value; } } }