【问题标题】:Ensuring type safety on generic lambda expression确保泛型 lambda 表达式的类型安全
【发布时间】:2017-05-15 17:37:52
【问题描述】:

这是对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);

而且,令我惊讶的是,这确实可以使用上面检索到的 getter:

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

但是,一旦我这样做了:

Long value = 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;
    }
}

【问题讨论】:

    标签: java generics reflection lambda java-8


    【解决方案1】:

    问题是你的方法

    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&lt;?&gt;Class&lt;R&gt; 的更改是纯编译时的事情,这就是为什么此时您应该得到一个“未经检查”的警告,至少在启用所有警告的情况下。

    在运行时进行真正检查的唯一方法是从调用者那里获取预期的类型:

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

    当然,这使得接受Field 的方法有点毫无意义。实际的getGetter 已经执行了一个查找,要求与 getter 的返回类型完全匹配,而要求字段类型和 getter 的返回类型必须匹配,您将一无所获。实际上,它对内部细节造成了不必要的依赖。

    为什么不简单地注释 getter 而不是字段...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多