简体   繁体   中英

Java: How do I specify a class of a class method argument?

I have a method whose signature is:

public static <T> T isA(Class<T> clazz);

So I can do this:

String str = isA(String.class);

Where I'm having trouble is if I want T to be Class<String> :

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

I'm not sure how to formulate the argument to isA() . Can anyone offer guidence?

In case you're wondering why I want to do this, I'm using EasyMock to mock a class that takes a Class<T> argument.


EDIT: I was asked to add an example of what I'm trying to do.

I'm trying to use EasyMock to mock Solr's SolrCore class as part of a test case. The signature of one of SolrCore's methods is:

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

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

With EasyMock I can set up an expectation for that method. The construct isA(PluginInfo.class) , for example, tells EasyMock to match any object of class PluginInfo:

 QueryParserPlugin queryPlugin = createNiceMock(QueryParserPlugin.class); SolrCore core = createNiceMock(SolrCore.class); expect(core.createInitInstance( isA(PluginInfo.class), isA(xxx), anyString(), anyString())).andReturn(queryPlugin); 

My problem is telling isA() to match any object of class Class<T> , where T in this case is QueryParserPlugin.

The issue with reflection and java is type erasure. You just need to give the compiler a hint.

Since the object you are expecting is of type T, and the method itself is generic, you kinda have to let java know what type you really are working with.

All it needs is a bit of a hint, something during runtime that can be passed through a compiled method that holds that type information.

So at compile time you have a method that takes in:

Class<String>

the compiler only knows the compiled type, so has no clue that the type itself is a class definition, making it impossible to assign if you don't tell java what the type of the assignment is.

So this works:

Class<String> myVar = String.class;

Or this works:

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

Or this works

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

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

but this doesn't work

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

since we have no assignment of T for the generic.

so how do you let the compiler know that T really is a class?

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

myMethod(myClassWrapper(String.class));

so by passing it through a method that accepts you let the compiler know that at minimum that this thing is a class and that it represents T at some part of T's own hierarchy, thus letting the method compile.

or of course you could always just do

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

but I think thats kinda hackish personally. I am not a fan of casts that are not explcit and wrapped in a method.

Since you cannot control the signature of the test framework, you can however let java know your intentions.

I am not sure how easy mock works, but heres a test to kinda help explain whats going on.

@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();
}

Though one big drawback is paramaterized types. But those can be solved using a guice type literal.

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

since its annon the type holds during runtime.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM