java - java 允许我将这个对象分配给这个通用字段吗?

标签 java generics reflection guava

在我的 POJO 编辑器中,我需要检查用户提供的类名是否可以实例化并分配给通用属性。 Guava 反射几乎提供了我需要的一切:我解析类型然后调用 proptype.isSupertypeOf(implClazz) ,但这并不像预期的那样工作。

例如,我有类型标记:java.util.Collection<java.sql.Timestamp>我希望我的支票接受 ArrayList和扩展原始 ArrayList 的类或 ArrayList<Timestamp>或它们自己被参数化,但不接受扩展 ArrayList<Integer> 的类.

然而,isSupertypeOf()仅适用于完全解析的类型,而不完整和原始类不被视为子类型。

    package com.common.jsp.beans;

    import java.lang.reflect.Field;
    import java.lang.reflect.Type;
    import java.sql.Timestamp;
    import java.util.ArrayList;
    import java.util.Collection;

    import com.google.common.reflect.TypeToken;

    public class TestTypeInf
    {
        public static class MyCollection1 extends ArrayList<Integer> {
        }
        public static class MyCollection2 extends ArrayList<Timestamp> {
        }
        public static class MyCollection3<T> extends ArrayList<T> {
        }
        public static class MyCollection4 extends ArrayList {
        }

        public static Collection<Timestamp> fld;

        public static void main( String[] args )
                        throws Exception
        {
            // bad: fld = new MyCollection1();
            fld = new MyCollection2();
            fld = new MyCollection3(); // just a warning
            fld = new MyCollection4(); // just a warning

            Field getter = TestTypeInf.class.getField( "fld" );
            Type memberType = getter.getGenericType();

            TypeToken<?> resolved = TypeToken.of( TestTypeInf.class ).resolveType( memberType );
            System.out.println( "type of fld: " + resolved );

            checkAssignable(resolved, MyCollection1.class);
            checkAssignable(resolved, MyCollection2.class);
            checkAssignable(resolved, MyCollection3.class); // This should be totally valid
            checkAssignable(resolved, MyCollection4.class); // why no?
        }

        private static void checkAssignable(TypeToken<?> resolved, Class<?> implClass) {
            System.out.println( "fld = new " + implClass.getSimpleName() + "()" );
            System.out.println( resolved.isSupertypeOf( implClass ) ? "yes" : "no" );
        }
    }

_

type of fld: java.util.Collection<java.sql.Timestamp>
fld = new MyCollection1()
no
fld = new MyCollection2()
yes
fld = new MyCollection3()
no
fld = new MyCollection4()
no
fld = new ArrayList()
no

最佳答案

这很 hacky,但由于您尝试做的事情也很 hacky(即试图反射(reflection)地确定在编译器中引发未经检查的警告的赋值),我认为这是合理的:

private static boolean isAssignable(TypeToken<?> resolved, Class<?> implClass) {
    return resolved.isSupertypeOf(implClass) || isAnySupertypeAssignable(resolved, implClass);
}

private static boolean isAnySupertypeAssignable(TypeToken<?> resolved, Class<?> implClass) {
    return TypeToken.of(implClass).getTypes().stream()
            .anyMatch(supertype -> isUncheckedSupertype(resolved, supertype));
}

private static boolean isUncheckedSupertype(TypeToken<?> resolved, TypeToken<?> implTypeToken) {
    if (implTypeToken.getType() instanceof ParameterizedType) {
        return false; // this prevents assignments of Collection<Integer> to Collection<Timestamp>
    }
    try {
        resolved.getSubtype(implTypeToken.getRawType());
        return true;
    } catch (IllegalArgumentException ex) {
        return false;
    }
}

我检查了你的例子,它返回:

fld = new MyCollection1() -> no
fld = new MyCollection2() -> yes
fld = new MyCollection3() -> yes
fld = new MyCollection4() -> yes
fld = new ArrayList() -> yes

关于java - java 允许我将这个对象分配给这个通用字段吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52019727/

相关文章:

java - JSF 2.2 - 未找到 "link"标签

java - 给定一个整数 N 作为输入,您可以检查以下内容 :

特定基类的 C++ 类模板

c# - 如何使用 Type.InvokeMember 调用显式实现的接口(interface)方法?

java - spring-security-oauth中的JdbcApprovalStore(ApprovalStore)有什么用?

java - 如何在 Java 中将 Mp3 转换为 Amr 音频格式

swift - 如何声明一个返回可迭代对象的函数

java - 如何从类对象中获取(通用)类型

c# - IEnumerable<T> 和反射

kotlin - 如何使用Kotlin Reflection按名称调用方法?