1

给定以下界面:

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.class的值。GenericInterface.TFixedImpl.class

我目前的想法:

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

但是这个过程有几个问题:

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

谁能指出我更容易/更可靠的方式来获取相同的信息?我似乎找不到一个,但我想我会问 toobs 的高智商:)

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

编辑:我最终制定了以下解决方案(在看到@stony-zhang's 之前):

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

3 回答 3

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,然后所有子类都从新的抽象类扩展,

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

    @Override
      public Class getMessageClass() {
           ...............
      }
}
于 2013-05-24T01:51:44.600 回答
0

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

Class<T> getTypeOfT();

并将其添加到您的FixedImpl

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

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

于 2013-05-24T00:57:53.010 回答
0

由于Type Erasure,我认为您无法获得可靠的结果:

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

您使用返回的对象类型的方法起初看起来不错,但除了您指出的问题之外,没有办法(在运行时)知道是否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

于 2013-05-24T01:00:51.260 回答