如何为字段创建功能接口实现?

wol*_*m77 4 java reflection java-8

考虑weightclass 中的一个字段Animal。我希望能够创建用于操作此字段的gettersetter功能接口对象。

class Animal {
  int weight;
}
Run Code Online (Sandbox Code Playgroud)

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

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();
}
Run Code Online (Sandbox Code Playgroud)

但这会产生以下错误:

java.lang.invoke.LambdaConversionException: Unsupported MethodHandle kind: getField x.Animal.weight:()int
Run Code Online (Sandbox Code Playgroud)

更新

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

可以用作Mapthrough 的示例类ObjectMap

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

这是预期的用法:

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
Run Code Online (Sandbox Code Playgroud)

Tun*_*aki 5

你让它变得太困难了。当你有一个 时Field,你可以直接调用unreflectGetter查找工厂来检索一个MethodHandle

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

public static Supplier<Object> 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);
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

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

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


Han*_*k D 5

函数式风格让你以新的方式思考这些事情。而不是基于反射的方法,例如

Supplier getter(Object obj, Class<?> cls, Field f){...}
Run Code Online (Sandbox Code Playgroud)

尝试类似的东西

static <O,F> Supplier<F> getter(O obj, Function<O,F> extractor) {
    return () -> extractor.apply(obj);
}
Run Code Online (Sandbox Code Playgroud)

你会像这样调用

Supplier<Integer> getWeight = getter(animal, a -> a.weight);
Integer weight = getWeight.get();
Run Code Online (Sandbox Code Playgroud)

a -> a.weight还有比想出一个过孔反射更难的吗Field

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

Supplier<Integer> getWeight = getter(animal, Animal::getWeight);
Run Code Online (Sandbox Code Playgroud)

类似的 setter 工厂可能是

static <O,F> Consumer<F> setter(O obj, BiConsumer<O,F> modifier) {
    return field -> modifier.accept(obj,field);
}
Run Code Online (Sandbox Code Playgroud)

像这样调用

Consumer<Integer> setWeight = setter(animal, (a, w) -> a.weight = w);
setWeight.accept(90);
Run Code Online (Sandbox Code Playgroud)