有没有一种更简单的方法可以在子类实现中检索硬编码的类型参数



给定以下接口:

public interface GenericInterface<T> {
    T getValue();
    void setValue(T newVal);
}

以及以下impl:

public class FixedImpl implements GenericInterface<String> {
    String value;
    public FixedImpl(String value) {
        this.value = value;
    }
    @Override
    public String getValue() {
        return value;
    }
    @Override
    public void setValue(String newVal) {
        value = newVal;
    }
}

我希望能够通过询问FixedImpl.class来确定在FixedImpl的情况下,String.classGenericInterface.T的值。

我目前的想法:

  1. GenericInterface中查找一个返回<T>的方法名称——在这种情况下,有"getValue"
  2. 遍历FixedImpl.class中使用相同名称声明的所有方法,并收集所有不同的返回类型
  3. Object最远的返回类型是我对GenericInterface.T的值

但这个过程有几个问题:

  • 它只适用于包含返回<T>的方法的泛型类型。使用setValue(T)不能安全地实现同样的技巧,因为在Java源代码中可以通过参数/aarity进行方法重载。它只适用于T getValue(),因为通过返回值重载是不可行的(除非我弄错了(
  • 它可能与Java8默认方法有奇怪的交互,或者在(仍然是泛型的(可能是抽象超类中实现泛型方法
  • 这有点杂乱无章

有人能给我指一个更容易/更可靠的方法来获得同样的信息吗?我似乎找不到一个,但我想我应该问问嘟嘟声中的智者:(

注意:如果你想知道我为什么需要这个,那是因为我想用类似的硬编码类型参数,但POJO值而不是简单的字符串,用程序构建容器类的mock。

编辑:我最终找到了以下解决方案(在看到@stony-zhang的之前(:

public static <G> List<Class> getConcreteTypes(Class<? extends G> implClass, Class<G> genericClass) {
    List<Class> concreteTypes = new ArrayList<Class>();
    for (Type type : implClass.getGenericInterfaces()) {
        if (!(type instanceof ParameterizedTypeImpl)) continue;
        ParameterizedTypeImpl parameterizedType = (ParameterizedTypeImpl) type;
        if (parameterizedType.getRawType() != genericClass) continue;
        for (Object arg : parameterizedType.getActualTypeArguments()) {
            if (!(arg instanceof Class))
                throw new IllegalArgumentException("Class " + implClass + " not concrete for generic type " + genericClass);
            concreteTypes.add((Class) arg);
        }
    }
    return concreteTypes;
}

您可以通过以下方式获得T的类,在接口中添加方法getMessageClass((,在FixedImpl中添加实现的方法

    @SuppressWarnings("rawtypes")
public Class getMessageClass() {
            int index =0; //In the case, you only have a generic type, so index is 0 to get the first one.
    Type genType = getClass().getGenericSuperclass();
    if (!(genType instanceof ParameterizedType)) {
        return Object.class;
    }
    Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
    if (index >= params.length || index < 0) {
        throw new RuntimeException("Index outof bounds");
    }
    if (!(params[index] instanceof Class)) {
        return Object.class;
    }
    return (Class) params[index];
}

在这种情况下,如果您有多个子类,要使用它,请创建一个抽象类来实现接口GenericInterface,然后所有子类都从新的抽象类扩展

public class abstract abstractImpl<T> implements implements GenericInterface<T> {
        @SuppressWarnings("rawtypes")
    @Override
      public Class getMessageClass() {
           ...............
      }
}

记住类型擦除。在运行时,不再有关于泛型的类型信息,除非您自己指定。这就是你应该做的。把这个添加到你的界面:

Class<T> getTypeOfT();

并将其添加到您的FixedImpl:中

@Override
public Class<String> getTypeOfT()
{
    return String.class;
}

这样,您就可以在GenericInterface<T>实现中始终调用getTypeOfT(),并了解您正在处理的类型。

我不认为你能得到可靠的结果,因为类型擦除:

  • 将泛型类型中的所有类型参数替换为其边界,如果类型参数是无边界的,则替换为Object。因此,生成的字节码只包含普通的类、接口和方法
  • 如有必要,插入类型强制转换以保持类型安全性
  • 生成桥接方法以保留扩展泛型类型中的多态性

您使用返回的对象类型的方法一开始可能看起来不错,但除了您指出的问题之外,(在运行时(没有办法知道The return type farthest from Object is my value for GenericInterface.T.

我的建议是使用某种配置XML,这种配置XML可以在构建时基于java源代码生成(使用Ant等构建工具(,然后用于创建Mock对象,或者您可以简单地在构建时根据源代码生成测试。

如果你不介意为了测试而更改运行时代码,Jan Doereenhaus的回答建议了一种简单的硬编码机制来检索类型

编辑:

考虑以下场景:

public class FixedImpl implements GenericInterface<SomeClass> {
    @Override
    public SomeClass getValue() {
        return new SomeClass();
    }
}
public class FixedImpl2 extends FixedImpl {
    @Override
    public SomeClass getValue()
    {
        return new SomeSubClass();
    }
}

从这个例子中,您可以看到FixedImpl的子类能够返回T的子类(它位于Object的继承层次结构的下游(

最新更新