2017-05-15 14 views
6

Đây là phần theo dõi trên Java8 retrieving lambda setter from class.Đảm bảo an toàn loại trên biểu thức lambda chung

Tôi đang cố gắng để có được một phương thức getter cho một lĩnh vực nhất định

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

Đây là phương pháp cơ bản:

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

này không biên dịch, do một loại không phù hợp:

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

Điều này tuy nhiên biên dịch:

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

Và, trước sự ngạc nhiên của tôi, điều này làm việc bằng cách sử dụng phương thức getter được lấy ra ở trên:

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

Tuy nhiên, một khi tôi làm điều này:

Long value = getter.get(testEntity); 

tôi nhận được ngoại lệ sau đây:

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) 

Có cách nào để nắm bắt điều này sớm hơn không?

Các TestEntity lớp:

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

Trả lời

7

Vấn đề là phương pháp của bạn

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

không thực sự thực hiện một kiểm tra. Về cơ bản bạn đang đúc một cá thể Class để nhập Class không có hiệu lực. Sự thay đổi kiểu chung Class<?> thành Class<R> là một điều biên dịch thuần túy, đó là lý do tại sao bạn sẽ nhận được cảnh báo "không được chọn" tại thời điểm này, ít nhất, nếu tất cả các cảnh báo được bật.

Cách duy nhất để làm một kiểm tra thực tế tại thời gian chạy, là để có được các loại dự kiến ​​từ người gọi:

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

Tất nhiên, nó làm cho các phương pháp chấp nhận một Field một chút vô nghĩa. Số thực tế getGetter sẽ thực hiện tra cứu yêu cầu đối sánh chính xác của loại trả về của getter và bạn không nhận được gì từ việc yêu cầu loại của trường và loại trả về của getter phải khớp. Trên thực tế, nó tạo ra một sự phụ thuộc không cần thiết cho các chi tiết nội bộ.

Tại sao không chỉ đơn giản là chú thích các getters thay vì các trường…

Các vấn đề liên quan