How to call a Custom Generic method by reflection in java?
class Person
{
public <T> void print(T t)
{
System.out.println(t.toString());
}
}
Generics are erased at compile time, they only provide extra information to the compiler to determine errors. They do not actually change the signature of the method in the .class file.
This means that you call a generic method by reflection in Java exactly the same way as you would call a non-generic method in Java, except that instead of specifying a type of T, you would specify a type of Object.
There are so many tutorials on how to call a regular method by reflection that I hesitate to add yet another; however, if you really need direction on how to call a method by reflection, please add a comment below and I'll add the necessary code.
If you find that things are not working as expected, you can always run javap on the compiled class file to verify that you are using the right objects in the argument list. It might be possible that if you specify a <T extends List> type generic signature, the resulting parameter object might actually be a List object.
This works for me.
Method method = Person.class.getMethod("print", Object.class);
method.invoke(new Person(), "this is a string");
method.invoke(new Person(), 273);
method.invoke(new Person(), new Object());
printing
this is a string
273
java.lang.Object#addbf1
Of course the theory behind this is explained beautifully in #Edwin's answer.
To highlight the point given in Edwin's answer, where we are using extends in a generic type: if you have a class like
GenericHibernateDao<T extends Serializable>
, and a method
public T save( T entity ) {};
to invoke the method save using reflection you have to use the Serializable class, i.e., you need to use:
Method method = GenericHibernateDao.class.getMethod(methodName, Serializable.class);
and not the Object.class as the parameter, since we are using
<T extends Serializable>
Related
How to call a Custom Generic method by reflection in java?
class Person
{
public <T> void print(T t)
{
System.out.println(t.toString());
}
}
Generics are erased at compile time, they only provide extra information to the compiler to determine errors. They do not actually change the signature of the method in the .class file.
This means that you call a generic method by reflection in Java exactly the same way as you would call a non-generic method in Java, except that instead of specifying a type of T, you would specify a type of Object.
There are so many tutorials on how to call a regular method by reflection that I hesitate to add yet another; however, if you really need direction on how to call a method by reflection, please add a comment below and I'll add the necessary code.
If you find that things are not working as expected, you can always run javap on the compiled class file to verify that you are using the right objects in the argument list. It might be possible that if you specify a <T extends List> type generic signature, the resulting parameter object might actually be a List object.
This works for me.
Method method = Person.class.getMethod("print", Object.class);
method.invoke(new Person(), "this is a string");
method.invoke(new Person(), 273);
method.invoke(new Person(), new Object());
printing
this is a string
273
java.lang.Object#addbf1
Of course the theory behind this is explained beautifully in #Edwin's answer.
To highlight the point given in Edwin's answer, where we are using extends in a generic type: if you have a class like
GenericHibernateDao<T extends Serializable>
, and a method
public T save( T entity ) {};
to invoke the method save using reflection you have to use the Serializable class, i.e., you need to use:
Method method = GenericHibernateDao.class.getMethod(methodName, Serializable.class);
and not the Object.class as the parameter, since we are using
<T extends Serializable>
I have a method call in a loop currently that does not compile:
for (Example example : Util.getExample(List.class)) {
// Do something with example
}
Util:
public class Util {
public <T> T getExample(Class<T> clazz) {
//...
}
}
The obvious fix is to cast the return from getExample to List<Example>. I'm wondering: is there is an alternative way to avoid the cast?
Further Information:
Posters asked for more information, so here goes...
I have built a framework around annotation processing that writes code to access and mutate class members (constructors, fields and methods). This framework backs both Parceler and Transfuse and allows me to, during compilation, identify a property and generate code to access or modify said property. For private properties (private constructors, private fields, private methods) I use a utility to perform these actions (Parceler's, Transfuse's) to break encapsulation via reflection.
Parceler has a feature to unroll collections during serialization in order to serialize members of the given collection. For private collections the InjectionUtil is used to access these properties within a loop:
for (Example example : InjectionUtil.getField(List.class, Container.class, container, "exampleField")) {
// ...
}
Which is the bug I'm currently faced with, and thus, why I'm asking about avoiding a cast. I'd prefer to not cast as I'd like to generically generate a bit of code to access a type and respect Java generics in the process.
If your getExample method is supposed to always return a list, then yes, change its return type to List<T>. But since you're passing List.class as an argument, it looks like you want to have a method that can return both lists and non-lists depending on which class object you pass it.
If so, that's not going to work the way you might be hoping. Your method in this case returns just List, the raw type. To make it return List<Example>, you'd have to pass it something like a hypothetical List<Example>.class, but there's no such thing. Generic type parameters are erased at compile time, so List<Example> and List<String> are really both the same class; they don't have separate class objects, so a class object argument can't tell your method what kind of list it should return.
You'll probably need to try a different design approach. Since this is clearly a simplified example, you might be able to get more help if you post more details about what you're actually trying to accomplish.
Guava's TypeToken can be used in this case because List<Foo>.class is not valid. TypeToken is used by creating an anonymous class. Because anonymous classes keep their generic signatures, this works.
for (Example foo : Util.getExample(new TypeToken<List<Example>>() {}) {
// do stuff
}
// utils
public <T> T getExample(TypeToken<T> typeToken) {
Type type = typeToken.getType();
// get example
}
TypeToken is more specific than just using the Class. You could also use the plain Type as a parameter so you can still feed it a Class. This is how Gson does it.
I think this is a design issue...
Since the method in Util you are calling is called getExamples it seems reasonable that it might just as well be fixed to return some collectionwhose elements are instance of the Example class.
It is reasonable to change getExamples to something like this?:
class Util {
public static <C extends Collection<? supper Example>> getExamples(final Supplier<C> factory) {
final C result = factory.get();
// here goes the code that adds the examples to the result collection
// using add or addAll.
return result;
}
}
So for-example if you wan to get a List<Example> using ArrayList<E> for implementation you would do like so:
List<Example> examples = Util.getExamples(ArrayList<Example>::new);
Try to pass the returned collection class object reference instead (eg. List.class, ArrayList.class) won't work as the code in getExamples will have a hard time (a) figuring out how to call the appropriate constructor using reflexion to instantiate the result (kinda of impossible if you pass just an interface class object such as List.class) and (b) casting the return from a raw type into a generic type with Example as element type. The latter is trivial however it is not as neat as it can be as it will generate a warning.
It is just more straight forward to delegate in the using code to indicate explicitly how to instantiate the result collection.
If you break away from returning a Collection and use methods like add and addAll in getExamples then perhaps you should borrow the Collectors framework from the java stream API.
I have a generic class as follows:
MyClass<T>
{
....
}
Now, what I want create a single generic method,say for example public MyClass<T> getMyClassInstance(Type t){...}, in which I pass a type and this method gives an instance of MyClass with that type.
See the following examples.
If I call this method as follows, getMyClassInstance(Integer_type), then it returns the MyClass<Integer> instance.
Similarly, if I call this method as follows, getMyClassInstance(String_type), then it returns the MyClass<String> instance.
How shall I write this method? If it can't be done, is there any alternative way or another better way to do something like this?
For simple applications of this, you don't even need to pass in a Type or Class argument. Consider the following:
public <T> List<T> getList() {
return new ArrayList<>();
}
To use this is as simple as:
List<Integer> lst = getList();
The compiler will infer the type of the list desired.
Do note that you will get more flexibility from passing in a Type parameter, especially in cases where you may not know in advance what sort of List you want to get back. But this sort of syntax, IMHO, reads more naturally and may be suited to what you want to do. (I say "may" since you haven't provided a ton of details ...)
The Type class must carry the type parameter on it: Type<T>, so you can declare
<T> MyClass<T> getMyClassInstance(Type<T> t);
If you can't make that happen, then the compiler is blind.
I extending an existing project that use generics in a few classes.
I am working on a class called PhiFunction, this class has one construcor which accepts a number of arguments. These include two arguments which I want to force to be the same type, E. These arguments are only used in the constructor, they are not stored as class fields.
I dont want to add this type to the class signature (I want to keep the class definition as simple as possible). The class signature just includes the type T at the moment. To keep the class signature as simple as possible, I added the definition of this type to the constructor signature, as shown below:
Simplified old code:
public class PhiFunction<T> {
...
public PhiFunction(
final MathematicalGroup<?> group, final List<?> baseElements, ...) {
Simplified new code:
public class PhiFunction<T> {
...
public <E extends GroupElement<T>> PhiFunction(
final MathematicalGroup<E> group, final List<E> baseElements, ...) {
The code compiles fine, and works fine.
The thing which surprises and confuses me now is that, it is still possible to create instances of PhiFunction as before. In other words, the addition of this type definition in the constructor signature does not change how the constructor is used (assuming that users do indeed supply arguments which use the same type). Users can still create instances of PhiFunction, just as they did before, without caring that this type definition was added to the constructor. I expected that users of this constructor would have to define E as being some particular class, but they dont.
I had not used generics in this way before. It seems that adding a type definition to the constructor (or to any method I guess) simply allows a type to be defined, without putting responsibilities on the user of the constructor to define that type.
I guess my question is, if the signature of a constructor includes the specification of a generic type, what extra responsibilities does that put on the user of the constructor?
I have one other related question. Should this type E be added to the Java docs as a parameter? and how? I am sure that it should be documented, as two of the arguments of the constructor must be of that type, but I am not sure how this should be added to the Javadocs.
You don't need to do any extra work for invoking a generic method or constructor. Most of the time, the compiler will be able to infer the type argument, based on the argument you pass to the method, or from the return type (not applicable here though).
So, when you create an instance of that class like this:
MathematicalGroup<Sometype> mathematicalGroup;
List<Sometype> list;
PhiFunction<Double> phiFunction = new PhiFunction<>(mathematicalGroup, list);
...the type parameter E of the constructor will automatically be inferred as Sometype.
However, if you pass arguments that doesn't comply to the rules of type parmeters, you'll see a compiler error, as in this case:
MathematicalGroup<Sometype> mathematicalGroup;
List<SomeOthertype> list;
// This will give a compiler error.
PhiFunction<Double> phiFunction = new PhiFunction<>(mathematicalGroup, list);
You also have the option to give explicit type arguments, if sometimes type inference doesn't work as expected:
// Redundant usage of explicit type argument
PhiFunction<Double> phiFunction =
new <Sometype>PhiFunction<Double>(mathematicalGroup, list);
Although the above usage of explicit type argument is redundant, but there are cases where compiler will not infer the type that you expect it to. Those are the cases when you pass inconsistent arguments to the parameters that are of same type parameter. Consider the below method for example:
public static <T> void fill( T [] array, T elem) {
for (int i=0; i<array.length; ++i) { array[i] = elem; }
}
.. if you try to invoke that method as:
fill(new String [5], new String ("XYZ")); // This is fine
fill(new String [5], new Integer (100)); // This is also fine? How?
Ideally you would expect the second method invocation to fail, because String and Integer should not be substitutable for same type parameter T. But here's the surprise. The compiler infers the type parameter as the intersection of all the super types of the arguments you passed. So, the type T is inferred as:
T:=Object&Serializable&Comparable
In that case, you might want to give <Object> as explicit type argument:
YourClass.<Object>fill(new String[5], new Integer(100));
P.S: Did you know that you can invoke non-generic methods in generic way? Well, generics is full of surprises :)
As for Javadoc, no you don't need to give any information about what E represent. Just explain what the paramters group and list means. That's it. The type parameter and formal parameters are anyways the part of the method signature, and will already be there. Consider for example Arrays.binarySearch() method.
To add it to Javadoc:
#param <E> This represents...
This constructor
public PhiFunction(
final MathematicalGroup<?> group, final List<?> baseElements, ...) {
Allows any type to be passed to/used with MathematicalGroup. Does MathematicalGroup have its own generics associated to it already?
If so, then perhaps your new constructor is redundant. That is, even though the original constructor has <?>, the generics restrictions of the MathematicalGroup class itself already does this enforcement.
public <E extends GroupElement<T>> PhiFunction(
final MathematicalGroup<E> group, final List<E> baseElements, ...) {
Otherwise, this is indeed more restrictive, as the original signature allows any type, and this one allows only GroupElement<T>, or one of its sub-classes.
As far as how your users use it:
MathematicalGroup mg = new <GroupElement<Object>>MathematicalGroup<Object>(groupElementInstance, ...);
This is an odd use-case. Usually this generics passing is done with static final utility functions.
I'm trying to use generic methods in java for the first time. It isn't working, and it appears to me that Java generics are simply not meant to be used this way - that I will need to implement a common interface rather then using a generic method. Please confirm (or deny) that conclusion.
This is what I'm trying to do:
public <T> void updateItem( T item ) {
String itemKey = item.getKey();
...
This gives me the error 'The method getKey() is undefined for the type T'.
Looking into why this doesn't work I see that type erasure "replaces all type parameters in generic types with their bounds or Object if the type parameters are unbounded".
The only way I can 'bound' my type T is if I create a common subclass or interface for all the types I was planning to use, and if I do that then I can use polymorphism rather then generics.
This is right. Method getKey() is not defined in class T. However if you define your method as following:
public <T extends Entry> void updateItem( T item ) {
String itemKey = item.getKey();
the compiler can find getKey() method. Please pay attention that I used <T extends Entry> (I mean java.util.Map.Entry). This class has method getKey() and compiler can resolve it.