2016-04-18 17 views
3

考慮類Animal中的字段weight。我希望能夠創建一個gettersetter用於操作此字段的功能接口對象。如何爲字段創建Functional接口實現?

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

我想創建一個類ObjectMap實施interface Map,基本上試圖表示一個對象作爲Map,其中對象可以是任何類型。目前正在使用Field.get()Field.set()用於get()put()方法操縱領域,並使用上述的方法來創建用於調用吸氣設定器方法SupplierConsumer對象。我想知道是否可以將兩種單獨的方法合併爲一個。

實例類,它可以作爲一個Map通過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; 
    } 
} 

這裏是預期使用:

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 

回答

3

您可以直接寫在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 <T> Supplier<T> getter(Object obj, Class<T> 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(); 
    a.a = 5; 

    Supplier<Integer> sup = getter(a, int.class, X.class.getField("a")); 

    assertEquals(5, sup.get().intValue()); 
} 
+0

謝謝。這確實解決了我的目的,但是你仍然在'Field.get()'中使用反射。在方法的情況下,我使用上述方法來獲取可以直接調用的接口對象。如果可以採取類似的方法,那將會很好。 – wolfram77

+1

那是你的出發點,傳遞'場'。你沒有說你是怎麼得到的。我不明白你對評論的額外要求。 – weston

+0

我確實需要一個供應商對象,但如果它速度更快,那就更好了。所以,期待一種比反思更快的方法。 – wolfram77

3

你快把它太困難,它必須這樣做。當你有一個Field,你可以直接調用unreflectGetter上查找工廠檢索MethodHandle

Produces a method handle giving read access to a reflected field. The type of the method handle will have a return type of the field's value type. If the field is static, the method handle will take no arguments. Otherwise, its single argument will be the instance containing the field.

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); 
     } 
    }; 
} 

這將返回字段的值的供應商。根據該字段的可訪問性,您可能需要調用setAccessible(true)

請注意,方法句柄和反射API也不同in terms of performance並且可能會更快。

+0

謝謝。從你指出的性能答案中,'plain'和'static_unreflect_invokeExact'一樣快,但是非最終的**非常慢。 – wolfram77

+0

哦,我只是注意到你在lambda函數裏面沒有反射,可能會很慢。 – wolfram77

+1

@ wolfram77是的,我認爲需要一個基準來判斷它是否更快。 – Tunaki

1

不能綁定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()); 
+0

在第一個例子中,你是否試圖從Field中創建一個'Supplier'。get()'函數?我不確定'LambdaMetaFactory'究竟是如何工作的,但是這個方法與@weston的答案類似,他也使用lambda表達式創建一個'Supplier'。然而,我從你的[先前的答案](http://stackoverflow.com/questions/195578​​29/faster-alternatives-to-javas-reflection/19563000#19563000)學到了這種方法(很久以前),它的速度和直接一樣快函數調用,用於方法。 – wolfram77

+0

它在語義上是等價的,只是保存了一個級別的委託,而不會打擾檢查的異常(它沒有任何性能差異)。因此,您可以使用weston的解決方案,*兩者*都具有以下優點:從JVM的角度來看,Field和目標對象保存在生成​​類的「final」字段中,以實現樂觀的內聯。如果你多次使用這個方法和不同的'Field's,我的解決方案有一個輕微的優勢,就是爲每個'Field'生成一個新的類,所以類型配置文件保持乾淨,但很難預測它是否有真正的好處 – Holger

+0

也許未來版本的「LambdaMetafactory」將支持現場處理,但是atm是我們可以獲得的最好... – Holger

1

功能風格讓你用新的方式思考這樣的事情。相反,像

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

基於反射的方式嘗試像

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

,你會調用像

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

a -> a.weight比任何想出通過反射一個Field更難?

一個優勢是,可以在需要時可以使用的字段或方法,例如,如果你增加了重量吸氣,

Supplier<Integer> getWeight = getter(animal, Animal::getWeight); 

類似的二傳手工廠可能是

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

調用這樣

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

對不起。這是我發佈的問題的一個很好的解決方案,但不是我真正需要的,因爲該類是* dynamic *,正如我在該問題的更新中提到的那樣。 – wolfram77

相關問題