Java:如何指定类方法参数的类?

时间:2014-04-07 22:53:27

标签: java generics

我有一个签名为:

的方法
public static <T> T isA(Class<T> clazz);

所以我可以这样做:

String str = isA(String.class);

我遇到麻烦的地方是我想要T Class<String>

Class<String> cls = isA(???);

我不确定如何将论据制定为isA()。任何人都可以提供指导吗?

如果您想知道我为什么要这样做,我会使用EasyMock来模拟一个带有Class<T>参数的类。

<小时/> 编辑:我被要求添加一个我正在尝试做的例子。

我正在尝试使用EasyMock模拟Solr的SolrCore类作为测试用例的一部分。 SolrCore方法之一的签名是:

public <T extends Object> T createInitInstance(PluginInfo info, Class<T> cast, String msg, String defClassName);

使用EasyMock,我可以为该方法设置期望。例如,构造isA(PluginInfo.class)告诉EasyMock匹配类PluginInfo的任何对象:

QueryParserPlugin queryPlugin = createNiceMock(QueryParserPlugin.class);
SolrCore core = createNiceMock(SolrCore.class);

expect(core.createInitInstance(
  isA(PluginInfo.class), isA(xxx),
  anyString(), anyString())).andReturn(queryPlugin);

我的问题是告诉isA()匹配类Class<T>的任何对象,其中T在这种情况下是QueryParserPlugin。

1 个答案:

答案 0 :(得分:2)

反射和java的问题是类型擦除。你只需要给编译器一个提示。

由于您期望的对象是T类型,并且该方法本身是通用的,因此您必须这样做 让java知道你真正使用的是什么类型。

所有它需要的是一些提示,在运行时可以通过一个包含该类型信息的编译方法传递。

所以在编译时你有一个方法:

Class<String>

编译器只知道编译类型,因此不知道类型本身是类定义,如果你不告诉java什么类型的赋值,就不可能分配。

这样可行:

Class<String> myVar = String.class;

或者这样做:

Class<String> myVar = isA(String.class);

或者这可行

public <T> T myMethod(Class<T> object)

Class<String> class = myMethod(String.class)

但这不起作用

public <T> void myMethod(Class<T> object);

因为我们没有为通用赋予T。

那么你怎么让编译器知道T真的是一个类?

public <T> void myClassWrapper(Class<? super T> object);

myMethod(myClassWrapper(String.class));

所以通过传递一个接受你的方法,让编译器知道至少这个东西是一个类,它在T自己的层次结构的某个部分表示T,从而让方法编译。

当然你也可以随时做

myMethod((Class<String>)string.class));

但我认为这有点像个人。我不是那些没有explcit并用方法包装的演员的粉丝。

由于您无法控制测试框架的签名,因此您可以让java了解您的意图。

我不确定模拟是否容易起作用,但是还有一个测试来帮助解释最新情况。

@Test
public void testCreation(){
    Object integer = 5;
    String myString = "A String";
    int five = typeTheObject(Integer.class, integer);
    Class<String> stringClass = typeTheObject(myString);
    Class<Integer> myInt = typeTheObject(five);
    Class<?> myClass = typeTheObject(String.class);
    TypeValidator typeValidator = new TypeValidator(stringClass);
    typeValidator.isA(typeTheObject(String.class));
}   

public static class TypeValidator{
    private final Object objectToValidate;
    public TypeValidator(Object object){
        objectToValidate = object;
    }

    public <T> T isA(T type){
        if(objectToValidate.getClass().isAssignableFrom(type.getClass())){
            return type;
        }else{
            Assert.fail();
            return null; //cuase 
        }
    }
}

public static <T> Class<T> typeTheObject(Class<? super T> type){
    return (Class<T>)type;
}

public static <T> T typeTheObject(Class<T> type, Object object){
    if(object.getClass().isAssignableFrom(type)){
        return (T)object;
    }
    return (T)object;
}

public static <T> Class<T> typeTheObject(Object object){
    return (Class<T>)((T)object).getClass();
}

虽然一个很大的缺点是参数化类型。但是那些可以使用guice类型的文字来解决。

(new TypeLiteral<List<String>(){}).getRawType();

因为它的类型在运行时保持类型。