generics type declaration for reflection

2019-09-09 02:46发布

问题:

I asked a question about differences between

public static <T> void work(Class<T> type, T instance);

and

public static <T, S extends T> void work(Class<T> type, S instance);

I think I should explain what exactly I want to know. Updating the original question is not proper for this time, I think, so I'm asking an another question here.

Say, I want to make a single reflected method for invoking those marshal methods defined in Marshaller such as

void marshal(Object element, ContentHandler handler)
void marshal(Object element, File output)
void marshal(Object element, Node node)

and so on.

One of method that I'm working on is

void marshal(Object jaxbElement, Class<?> targetType, Object target)

The implementation is simple

  1. Find a method looks like marshal(Ljava/lang/Object;Ljava/lang/Class;)V with Object.class and targetType
  2. invoke the method with element and target.

So any unit testing code can invoke like

marshal(element, ContentHandler.class, handler);
marshal(element, File.class, new File("text.xml"));

In this case how should I define the marshal method? Is there any difference between

<T> marshal(Object element, Class<T> targetType, T target);

and

<T, S extends T> marshal(Object element, Class<T> targetType, S target)

?

Further Comments per Answers

I think I need the targetType for fast and direct method looking the right method.

Without the targetType I have to iterate all methods like

for (Method method : Marshaller.class.getMethods()) {

    // check modifiers, name, return type, and so on.

    if (!method.getParameterTypes()[1].isAssignableFrom(target.getClass())) {
    }
}

Adding another version for this will be better, I think. :)

回答1:

It's hard to know why you want the class object, because if you have an instance of T you can do this in the method:

Class<T> targetType = target.getClass(); // class may be derived from the instance    

However, if you really need to pass a class, I think you want this:

<T> void marshal(Object element, Class<? super T> targetType, T target) {

}

By using Class<? super T> you achieve the flexibility of passing a Class that is a super class of the instance without the extra generic method parameter.



回答2:

If you assign the class at T, you don't need to feed it in the arguments, so <T, S extends T> marshal(Object element, S target) should be enough. But since S extends T, S can be recognized as T.

Unless you specifically need methods that are only in class S, you can omit it and write

<T> marshal(Object element, T target)

However, since you are creating a generic method, you probably won't need to declare S. This for the following reason:

  • if the methods of S differ from T, there is no good way to determine the methods of several different classes of S. Unless they have a common interface, but then you might as well use the common interface as T.

  • if the methods of S do not differ, there is not reason to specifically indicate it as generic operand.



回答3:

There is no difference in what sets of arguments that

<T> marshal(Object element, Class<T> targetType, T target);

and

<T, S extends T> marshal(Object element, Class<T> targetType, S target)

can accept.

Therefore, if you are writing an API, you should prefer the simpler one, the one with fewer type parameters, i.e. the one without S.