有没有更简单的方法来检索子类实现中的硬编码类型参数?

时间:2013-05-24 00:44:50

标签: java generics reflection

给出以下界面:

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的情况下,String.classGenericInterface.T的值,通过询问FixedImpl.class

我目前的想法:

  1. GenericInterface中找到一个返回<T>的方法名称 - 在这种情况下,有"getValue"
  2. 浏览FixedImpl.class中使用相同名称声明的所有方法,并收集所有不同的返回类型。
  3. 距离Object最远的返回类型是我GenericInterface.T的值。
  4. 但是这个过程存在一些问题:

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

    有人能指点我更简单/更可靠的方式来获取相同的信息吗?我似乎无法找到一个,但我想我会问问题的优秀知识分子:)

    NB:如果你想知道我为什么需要这个,那是因为我想以编程方式构造具有类似硬编码类型参数的容器类的模拟,但是POJO值而不是简单的字符串

    编辑:我最终制定了以下解决方案(在看到@ 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;
    }
    

3 个答案:

答案 0 :(得分:1)

你可以通过以下方式获取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,然后all子类从新的抽象类扩展,

public class abstract abstractImpl<T> implements implements GenericInterface<T> {
        @SuppressWarnings("rawtypes")

    @Override
      public Class getMessageClass() {
           ...............
      }
}

答案 1 :(得分:0)

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

Class<T> getTypeOfT();

并将其添加到您的FixedImpl

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

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

答案 2 :(得分:0)

由于Type Erasure

,我认为您无法获得可靠的结果
  
      
  • 如果类型参数是无界的,则将泛型类型中的所有类型参数替换为其边界或对象。因此,生成的字节码只包含普通的类,接口和方法。
  •   
  • 如有必要,插入类型转换以保护类型安全。
  •   
  • 生成桥接方法以保留扩展泛型类型中的多态性。
  •   

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

我的建议是使用某种配置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的继承层次结构的下方)