2017-05-15 9 views
6

これはJava8 retrieving lambda setter from classのフォローアップの一部です。ジェネリックラムダ式の型の安全性を確保する

私は与えられたフィールドのgetterメソッドを取得しようとしている

public <T, R> IGetter<T, R> getGetter(Class<T> clazz, Field field) { 

    Class<R> fieldType = null; 
    try { 
     fieldType = (Class<R>) field.getType(); 
    } catch(ClassCastException e) { 
     error("Attempted to create a mistyped getter for the field " + field + "!"); 
    } 

    return getGetter(clazz, field.getName(), fieldType); 
} 

これは根本的な方法である:

public <T, R> IGetter<T, R> getGetter(Class<T> clazz, String fieldName, Class<R> fieldType) { 

    MethodHandles.Lookup caller = null; 
    MethodHandle target = null; 
    MethodType func = null; 

    try { 
     caller = MethodHandles.lookup(); 
     MethodType getter = MethodType.methodType(fieldType); 
     target = caller.findVirtual(clazz, computeGetterName(fieldName), getter); 
     func = target.type(); 
    } catch (NoSuchMethodException e) { 
     error("Could not locate a properly named getter \"" + computeGetterName(fieldName) + "\"!"); 
    } catch (IllegalAccessException e) { 
     error("Could not access \"" + computeGetterName(fieldName) + "\"!"); 
    } 

    CallSite site = null; 
    try { 
     site = LambdaMetafactory.metafactory(
       caller, 
       "get", 
       MethodType.methodType(IGetter.class), 
       func.generic(), 
       target, 
       func 
     ); 
    } catch (LambdaConversionException e) { 
     error("Could not convert the getter \"" + computeGetterName(fieldName) + "\" into a lambda expression!"); 
    } 

    MethodHandle factory = site.getTarget(); 

    IGetter<T, R> r = null; 
    try { 
     r = (IGetter<T, R>) factory.invoke(); 
    } catch (Throwable throwable) { 
     error("Casting the factory of \"" + computeGetterName(fieldName) + "\" failed!"); 
    } 

    return r; 
} 

これは、型の不一致によるコンパイルされません。

IGetter<TestEntity, Long> getter = accessorFactory.getGetter(TestEntity.class, "name", String.class); 

これはコンパイルします。

Field field = TestEntity.class.getDeclaredField("name"); 
IGetter<TestEntity, Long> getter = accessorFactory.getGetter(TestEntity.class, field); 

そして、驚いたことに、これは、上記取得されたゲッター使用して作業を行います。

Long value = getter.get(testEntity); 

私は次の例外を取得:私はこれを行うと、しかし

TestEntity testEntity = new TestEntity(1L, "Test"); 
System.out.println(getter.get(testEntity)); 

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Long 
    at de.cyclonit.exercise.Main.main(Main.java:26) 

これを先にキャッチする方法はありますか?

TestEntityクラス:

public class TestEntity { 

    private Long id; 

    private String name; 


    public TestEntity(Long id, String name) { 
    this.id = id; 
     this.name = name; 
    } 


    public Long getId() { 
     return id; 
    } 

    public void setId(Long id) { 
     this.id = id; 
    } 

    public String getName() { 
     return name; 
    } 
} 

答えて

7

問題は、あなたの方法

public <T, R> IGetter<T, R> getGetter(Class<T> clazz, Field field) { 
    Class<R> fieldType = null; 
    try { 
     fieldType = (Class<R>) field.getType(); 
    } catch(ClassCastException e) { 
     error("Attempted to create a mistyped getter for the field " + field + "!"); 
    } 
    return getGetter(clazz, field.getName(), fieldType); 
} 

が実際にチェックを行っていないということです。基本的にClassインスタンスをタイプしてClassと入力しても効果はありません。汎用タイプClass<?>からClass<R>への変更は純粋なコンパイル時のものなので、少なくとも警告がすべて有効になっている場合は、この時点で「未チェック」の警告が表示されるはずです。それは少し無意味Fieldを受け入れる方法を行い、もちろん

public <T, R> IGetter<T, R> getGetter(Class<T> clazz, Class<R> fieldType, Field field) { 
    if(fieldType != field.getType()) { 
     error("Attempted to create a mistyped getter for the field " + field + "!"); 
    } 
    return getGetter(clazz, field.getName(), fieldType); 
} 

、実行時に実際のチェックを行うための唯一の方法は、発信者から期待されるタイプを取得することです。実際のgetGetterはすでにゲッターの戻り値の型と完全に一致するルックアップを実行します。フィールドの型とgetterの戻り値の型が一致している必要はありません。実際には、内部の詳細への不要な依存関係を作成します。

フィールドの代わりに単にゲッターに注釈を付けるだけでは...

関連する問題