binding to the right constructor at runtime - Java - java

I came up with a solution today involving creating classes at runtime, after parsing a file, using the Reflection API in Java.
while ((line = textReader.readLine()) != null)
{
Pattern p = Pattern
.compile("([^:]+):([^:]+)::([\\d]+)::([^:]+)::(.+)");
Matcher m = p.matcher(line);
if (m.find())
{
String id = m.group(1);
String className = m.group(2);
int orderOfExecution = Integer.valueOf(m.group(3));
String methodNameOrNew = m.group(4);
Object[] arguments = m.group(5).split("::");
if (methodNameOrNew.compareTo("new") == 0)
{
System.out.println("Loading class: " + className);
if (className.contains("Competition"))
{
continue;
}
else if (className.contains("$"))
{
continue;
}
else
{
Class<?> cl = Class.forName(className);
printMembers(cl.getConstructors(), "Constructor");
Constructor<?>[] cons = cl.getConstructors();
Object obj = cons[0].newInstance(arguments);
this.map.put(id, obj);
}
}
}
}
and printMembers():
private static void printMembers(Member[] mbrs, String s)
{
out.format("%s:%n", s);
for (Member mbr : mbrs)
{
if (mbr instanceof Field)
out.format(" %s%n", ((Field) mbr).toGenericString());
else if (mbr instanceof Constructor)
out.format(" %s%n", ((Constructor) mbr).toGenericString());
else if (mbr instanceof Method)
out.format(" %s%n", ((Method) mbr).toGenericString());
}
if (mbrs.length == 0)
out.format(" -- No %s --%n", s);
out.format("%n");
}
However, I get the following error:
Loading class: org.powertac.common.TariffSpecification
Constructor:
public org.powertac.common.TariffSpecification(org.powertac.common.Broker,org.powertac.common.enumerations.PowerType)
java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
at Parser.parse(Parser.java:64)
at Parser.main(Parser.java:137)
arguments[] is : 1 : CONSUMPTION. How could I create the right constructor, and give it the right arguments (types) ?
For example, in the sample parser I'm using I have:
2233:org.powertac.common.Tariff::6::new::6
then I have to create a class of the type org.powertac.common.Tariff (new tells me a new object needs to be created, and it takes a double rate as argument, in this case 6. However, I don't know it takes a double, only the argument is String (6). How could I create / convert / cast to the correct type and then assign it to the constructor? My first thought was to create a symbol table, but I'm wondering about an easier solution...

You need to use Class.getConstructor(Class...) to choose the constructor that is appropriate for arguments you wish to pass in to Constructor.newInstance(Object...)
In your example I'm going to assume an array of 1 : CONSUMPTION means you have an array equivalent to
Object[] arguments = new Object[]{Integer.valueOf(1), "CONSUMPTION"};
So you call the following
Class clazz = ... //Whatever class reference you have
Constructor c = clazz.getConstructor(Integer.class, String.class);
Object obj = c.newInstance(arguments);
If you don't know the types of your arguments you will have to test the argument set against the Class array returned by Constructor.getParameterTypes() for each constructor returned by Class.getConstructors() until you find a constructor that matches your argument array. More specifically, the array of arguments and array of classes are the same length and each class in the class array passes Class.isAssignableFrom(Class) for the class of the value in the same position in the argument array.
Implementation of above in code
public boolean canConstruct(Object[] args, Constructor<?> c){
Class<?>[] paramTypes = c.getParameterTypes();
if(args.length != paramTypes.length){
return false;
}
int i = 0;
for(Object arg: args){
if(!paramTypes[i].isAssignableFrom(arg.getClass())){
return false;
}
i++;
}
return true;
}
In order to use this you will have to have your argument array as you want to pass it to the constructor. You could try to edit your input so that it includes type information (this is similar to how java serialization works) so that you can construct the arguments for the constructor argument array via reflection with their own type constructors

Related

Create object using Java reflection

I receive from an external source strings that are pretty much the signature of the constructor of the object. They contain the class name and the parameters.
For example:
public class Foo {
public Foo(int x, int y, int z) {
// do something
}
}
public class Bar {
public Bar (int x, boolean bool) {
// do something
}
}
Let's say I have many classes like this, and I want to create object from these classes based on the string I receive (that contains the class name and the paramters). Can this be done?
I know I can get the class using something like this:
String className = "myClass";
Class<?> myClass = Class.forName(className);
//Constructor<?> ctor = myClass.getConstructor();
//Object object = ctor.newInstance(new Object[] { ctorArgument });
But how can I instantiate the object if I don't know at compile time how many paramters the constructor will have and of which type they will be? By the way, the classes from which I want to create the object will most likely have only one constructor available. But each class might have a different constructor compared to the other.
No problem because the newInstance() method receives an array:
public T newInstance(Object ... initargs)
So you just initialize an array containing all the arguments and pass it
something like this:
String[] argsAsString = readValuesFromString(data);
Object[] args = new Object[argAsString.length()];
int i = 0;
Class<?>[] paramTypes = ctor.getParameterTypes();
for (String argStr: argsAsString) {
args[i] = convertStringToActualType(argStr, paramTypes[i]);
i++;
}
ctor.newInstance(args);
You probably need to call getConstructors() on the class and then try to find the constructor with the correct number of arguments. This will work only if you dont have two or more constructors with the same number of arguments.
Then the convertStringToActualType() should know how to convert the string value to the actual parameter.. for example if it's boolean field `Boolean.valueOf(argStr)' etc - example:
Object convertStringToActualType(String arg, Class<?> type) {
if (type == String.class)
return arg;
if (type == Boolean.class)
return Boolean.valueOf(arg);
throw new IllegalArgumentException("type " + type.getSimpleName() + " not supported");
}

How to get parameter types using reflection?

I want to use functions having different numbers of parameters. The problem is that I don't know the number of parameters of each function, and also I don't know names of function as they are stored in an array. I only knows the class name, but don't want to use getDeclaredMethods as it will increase search time. Is there a way to get the parameter types for each function?
What I usually do when I have to look up methods is to generate a cache key from the query I am doing and save the search result with this cache key in a map.
Example:
I know the method parameters are Boolean.TRUE, Arrays.asList("foo","bar","baz") and BigInteger.valueOf(77777l)
My class contains a method with the signature
public foo(boolean, Collection, Number)
There's no way I can directly map the parameters to the parameter types because I just don't know which of the super classes or interfaces is the parameter type as you can see from the following table:
Expected Type | What I have
-----------------------------------------------------
boolean | java.lang.Boolean
java.util.Collection | java.util.Arrays$ArrayList
java.lang.Number | java.math.BigInteger
Each of these pairs is compatible, but there's no way to find the compatible method without defining a comparison method, something like this:
// determine whether a method's parameter types are compatible
// with my arg array
public static boolean isCompatible(final Method method,
final Object[] params) throws Exception{
final Class<?>[] parameterTypes = method.getParameterTypes();
if(params.length != parameterTypes.length){
return false;
}
for(int i = 0; i < params.length; i++){
final Object object = params[i];
final Class<?> paramType = parameterTypes[i];
if(!isCompatible(object, paramType)){
return false;
}
}
return true;
}
// determine whether a single object is compatible with
// a single parameter type
// careful: the object may be null
private static boolean isCompatible(final Object object,
final Class<?> paramType) throws Exception{
if(object == null){
// primitive parameters are the only parameters
// that can't handle a null object
return !paramType.isPrimitive();
}
// handles same type, super types and implemented interfaces
if(paramType.isInstance(object)){
return true;
}
// special case: the arg may be the Object wrapper for the
// primitive parameter type
if(paramType.isPrimitive()){
return isWrapperTypeOf(object.getClass(), paramType);
}
return false;
}
/*
awful hack, can be made much more elegant using Guava:
return Primitives.unwrap(candidate).equals(primitiveType);
*/
private static boolean isWrapperTypeOf(final Class<?> candidate,
final Class<?> primitiveType) throws Exception{
try{
return !candidate.isPrimitive()
&& candidate
.getDeclaredField("TYPE")
.get(null)
.equals(primitiveType);
} catch(final NoSuchFieldException e){
return false;
} catch(final Exception e){
throw e;
}
}
So what I'd do is have a method cache:
private static final Map<String, Set<Method>> methodCache;
and add a lookup method like this:
public static Set<Method> getMatchingMethods(final Class<?> clazz,
final Object[] args) throws Exception{
final String cacheKey = toCacheKey(clazz, args);
Set<Method> methods = methodCache.get(cacheKey);
if(methods == null){
final Set<Method> tmpMethods = new HashSet<Method>();
for(final Method candidate : clazz.getDeclaredMethods()){
if(isCompatible(candidate, args)){
tmpMethods.add(candidate);
}
}
methods = Collections.unmodifiableSet(tmpMethods);
methodCache.put(cacheKey, methods);
}
return methods;
}
private static String toCacheKey(final Class<?> clazz, final Object[] args){
final StringBuilder sb = new StringBuilder(clazz.getName());
for(final Object obj : args){
sb.append('-').append(
obj == null ? "null" : obj.getClass().getName());
}
return sb.toString();
}
That way, subsequent lookups will take much less time than the first one (for parameters of the same type).
Of course since Class.getDeclaredMethods() uses a cache internally, the question is whether my cache improves performance at all. It's basically a question of what's faster:
generating a cache key and querying a HashMap or
iterating over all methods and querying for parameter compatibility
My guess: for large classes (many methods), the first method will win, otherwise the second will

Java Reflection calling constructor with primitive types

I have a method in my test framework that creates an instance of a class, depending on the parameters passed in:
public void test(Object... constructorArgs) throws Exception {
Constructor<T> con;
if (constructorArgs.length > 0) {
Class<?>[] parameterTypes = new Class<?>[constructorArgs.length];
for (int i = 0; i < constructorArgs.length; i++) {
parameterTypes[i] = constructorArgs[i].getClass();
}
con = clazz.getConstructor(parameterTypes);
} else {
con = clazz.getConstructor();
}
}
The problem is, this doesn't work if the constructor has primitive types, as follows:
public Range(String name, int lowerBound, int upperBound) { ... }
.test("a", 1, 3);
Results in:
java.lang.NoSuchMethodException: Range.<init>(java.lang.String, java.lang.Integer, java.lang.Integer)
The primitive ints are auto-boxed in to object versions, but how do I get them back for calling the constructor?
Use Integer.TYPE instead of Integer.class.
As per the Javadocs, this is "The Class instance representing the primitive type int."
You can also use int.class. It's a shortcut for Integer.TYPE. Not only classes, even for primitive types you can say type.class in Java.
To reference primitive types use, for example:
Integer.TYPE;
You will need to know which arguments passed into your method are primitive values. You can do this with:
object.getClass().isPrimitive()
Since the primitive types are autoboxed, the getConstructor(java.lang.Class<?>... parameterTypes) call will fail. You will need to manually loop through the available constructors. If all types match then you're fine. If some types do not match, but the required type is a primitive AND the available type is the corresponding wrapper class, then you can use that constructor. See bellow:
static <C> Constructor<C> getAppropriateConstructor(Class<C> c, Object[] initArgs){
if(initArgs == null)
initArgs = new Object[0];
for(Constructor con : c.getDeclaredConstructors()){
Class[] types = con.getParameterTypes();
if(types.length!=initArgs.length)
continue;
boolean match = true;
for(int i = 0; i < types.length; i++){
Class need = types[i], got = initArgs[i].getClass();
if(!need.isAssignableFrom(got)){
if(need.isPrimitive()){
match = (int.class.equals(need) && Integer.class.equals(got))
|| (long.class.equals(need) && Long.class.equals(got))
|| (char.class.equals(need) && Character.class.equals(got))
|| (short.class.equals(need) && Short.class.equals(got))
|| (boolean.class.equals(need) && Boolean.class.equals(got))
|| (byte.class.equals(need) && Byte.class.equals(got));
}else{
match = false;
}
}
if(!match)
break;
}
if(match)
return con;
}
throw new IllegalArgumentException("Cannot find an appropriate constructor for class " + c + " and arguments " + Arrays.toString(initArgs));
}
you can write
int[].class.getComponentType()
or
Integer.TYPE
or
int.class
If primitive int value is autoboxed into Integer object, it's not primitive anymore. You can't tell from Integer instance whether it was int at some point.
I would suggest passing two arrays into test method: one with types and another with values. It'll also remove ambiguity if you have a constructor MyClass(Object) and pass string value (getConstructor would be looking for String constructor).
Also, you can't tell expected parameter type if parameter value is null.
To actually check if a type is a primitive or it's wrapper use:
ClassUtils.isPrimitiveOrWrapper(memberClazz)
In the case you want to check if it's a specific type take a look at this:
https://stackoverflow.com/a/27400967/2739334
In any case #Andrzej Doyle was completely right!

With Java reflection how to instantiate a new object, then call a method on it?

I'm pretty new to Java, and I'm facing a reflection issue.
Let's say i have to dynamically call the method fooMethod on an instance of the class Foobar
I got so far an instance of Foobar with:
Object instance = Class.forName("Foobar").newInstance();
Let's say I know there's a method fooMethod on this object (I can even check this with Class.forName("Foobar").getDeclaredMethods() ) , how to call it, please?
Method method = getClass().getDeclaredMethod("methodName");
m.invoke(obj);
This is in case the method doesn't have arguments. If it has, append the argument types as arguments to this method.
obj is the object you are calling the method on.
See the java.lang.Class docs
You can use reflection
sample class
package com.google.util;
class Maths {
public Integer doubleIt(Integer a) {
return a*2;
}
}
and use something like this-
step 1:- Load class with given input name as String
Class<?> obj = Class.forName("Complete_ClassName_including_package");
//like:- Class obj = Class.forName("com.google.util.Maths");
step 2:- get Method with given name and parameter type
Method method = obj.getMethod("NameOfMthodToInvoke", arguments);
//arguments need to be like- `java.lang.Integer.class`
//like:- Method method= obj.getMethod("doubleIt", java.lang.Integer.class);
step 3:- invoke Method by passing instance of Object and argument
Object obj2 = method.invoke(obj.newInstance(), id);
//like :- method.invoke(obj.newInstance(), 45);
YOU CAN DO STEP 2 LIKE THIS ALSO
(when you do not know particular method exists in a class you check all method by looping method's array)
Method[] methods = obj.getMethods();
Method method = null;
for(int i=0; i < methods.length(); i++) {
if(method[1].getName().equals("methodNameWeAreExpecting")) {
method = method[i];
}
}
Purely reflection: Method.invoke.
The other solution is to require the item you are reflectively creating to implement a known interface and cast to this interface and use as normal.
The latter is commonly used for "plugins", the former is not used very often.
You can start by reading about it here.
As for the code you are after it is like this (from the same resource):
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods) {
String mname = m.getName();
if (!mname.startsWith("test")
|| (m.getGenericReturnType() != boolean.class)) {
continue;
}
Type[] pType = m.getGenericParameterTypes();
if ((pType.length != 1)
|| Locale.class.isAssignableFrom(pType[0].getClass())) {
continue;
}
out.format("invoking %s()%n", mname);
try {
m.setAccessible(true);
Object o = m.invoke(t, new Locale(args[1], args[2], args[3]));
out.format("%s() returned %b%n", mname, (Boolean) o);
// Handle any exceptions thrown by method to be invoked.
} catch (InvocationTargetException x) {
Throwable cause = x.getCause();
err.format("invocation of %s failed: %s%n",
mname, cause.getMessage());
}
This should work for you:
((Foobar)instance).fooMethod()
Class.forName("Foobar").newInstance();
is now deprecated (https://docs.oracle.com/javase/9/docs/api/java/lang/Class.html#forName-java.lang.Module-java.lang.String-)
Class.forName("Foobar").getDeclaredConstructor().newInstance()
or if you need a specific constructor:
Constructor constructor = Class.forName("java.lang.String").getConstructor(String.class);
String object = (String) constructor.newInstance("Hello");

How to test whether method return type matches List<String>

What is the easiest way to test (using reflection), whether given method (i.e. java.lang.Method instance) has a return type, which can be safely casted to List<String>?
Consider this snippet:
public static class StringList extends ArrayList<String> {}
public List<String> method1();
public ArrayList<String> method2();
public StringList method3();
All methods 1, 2, 3 fulfill the requirement. It's quite easy to test it for the method1 (via getGenericReturnType(), which returns instance of ParameterizedType), but for methods2 and 3, it's not so obvious. I imagine, that by traversing all getGenericSuperclass() and getGenericInterfaces(), we can get quite close, but I don't see, how to match the TypeVariable in List<E> (which occurs somewhere in the superclass interfaces) with the actual type parameter (i.e. where this E is matched to String).
Or maybe is there a completely different (easier) way, which I overlook?
EDIT: For those looking into it, here is method4, which also fulfills the requirement and which shows some more cases, which have to be investigated:
public interface Parametrized<T extends StringList> {
T method4();
}
I tried this code and it returns the actual generic type class so it seems the type info can be retrieved. However this only works for method 1 and 2. Method 3 does not seem to return a list typed String as the poster assumes and therefore fails.
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
try{
Method m = Main.class.getDeclaredMethod("method1", new Class[]{});
instanceOf(m, List.class, String.class);
m = Main.class.getDeclaredMethod("method2", new Class[]{});
instanceOf(m, List.class, String.class);
m = Main.class.getDeclaredMethod("method3", new Class[]{});
instanceOf(m, List.class, String.class);
m = Main.class.getDeclaredMethod("method4", new Class[]{});
instanceOf(m, StringList.class);
}catch(Exception e){
System.err.println(e.toString());
}
}
public static boolean instanceOf (
Method m,
Class<?> returnedBaseClass,
Class<?> ... genericParameters) {
System.out.println("Testing method: " + m.getDeclaringClass().getName()+"."+ m.getName());
boolean instanceOf = false;
instanceOf = returnedBaseClass.isAssignableFrom(m.getReturnType());
System.out.println("\tReturn type test succesfull: " + instanceOf + " (expected '"+returnedBaseClass.getName()+"' found '"+m.getReturnType().getName()+"')");
System.out.print("\tNumber of generic parameters matches: ");
Type t = m.getGenericReturnType();
if(t instanceof ParameterizedType){
ParameterizedType pt = (ParameterizedType)t;
Type[] actualGenericParameters = pt.getActualTypeArguments();
instanceOf = instanceOf
&& actualGenericParameters.length == genericParameters.length;
System.out.println("" + instanceOf + " (expected "+ genericParameters.length +", found " + actualGenericParameters.length+")");
for (int i = 0; instanceOf && i < genericParameters.length; i++) {
if (actualGenericParameters[i] instanceof Class) {
instanceOf = instanceOf
&& genericParameters[i].isAssignableFrom(
(Class) actualGenericParameters[i]);
System.out.println("\tGeneric parameter no. " + (i+1) + " matches: " + instanceOf + " (expected '"+genericParameters[i].getName()+"' found '"+((Class) actualGenericParameters[i]).getName()+"')");
} else {
instanceOf = false;
System.out.println("\tFailure generic parameter is not a class");
}
}
} else {
System.out.println("" + true + " 0 parameters");
}
return instanceOf;
}
public List<String> method1() {
return null;
}
public ArrayList<String> method2() {
return new ArrayList<String>();
}
public StringList method3() {
return null;
}
public <T extends StringList> T method4() {
return null;
}
This outputs:
Testing method: javaapplication2.Main.method1
Return type test succesfull: true (expected 'java.util.List' found 'java.util.List')
Number of generic parameters matches: true (expected 1, found 1)
Generic parameter no. 1 matches: true (expected 'java.lang.String' found 'java.lang.String')
Testing method: javaapplication2.Main.method2
Return type test succesfull: true (expected 'java.util.List' found 'java.util.ArrayList')
Number of generic parameters matches: true (expected 1, found 1)
Generic parameter no. 1 matches: true (expected 'java.lang.String' found 'java.lang.String')
Testing method: javaapplication2.Main.method3
Return type test succesfull: false (expected 'java.util.List' found 'com.sun.org.apache.xerces.internal.xs.StringList')
Number of generic parameters matches: true 0 parameters
Testing method: javaapplication2.Main.method4
Return type test succesfull: true (expected 'com.sun.org.apache.xerces.internal.xs.StringList' found 'com.sun.org.apache.xerces.internal.xs.StringList')
Number of generic parameters matches: true 0 parameters
Solving this in general is really not easy to do yourself using only the tools provided by Java itself. There are a lot of special cases (nested classes, type parameter bounds,...) to take care of.
That's why I wrote a library to make generic type reflection easier: gentyref. I added sample code (in the form of a JUnit test) to show how to use it to solve this problem: StackoverflowQ182872Test.java. Basically, you just call GenericTypeReflector.isSuperType using a TypeToken (idea from Neil Gafter) to see if List<String> is a supertype of the return type.
I also added a 5th test case, to show that an extra transformation on the return type (GenericTypeReflector.getExactReturnType) to replace type parameters with their values is sometimes needed.
This thread on the java.net forums might be helpful (although I have to admit I didn't understand everything they said).
I think you are already on the right track. Just keep using getGenericSuperclass() and getGenericInterface() until you start getting parameterized types back...
So basically:
//For string list
ParameterizedType type = (ParameterizedType)StringList.class.getGenericSuperclass();
System.out.println( type.getActualTypeArguments()[0] );
//for a descendant of string list
Class clazz = (Class)StringListChild.class.getGenericSuperclass();
ParameterizedType type = (ParameterizedType)clazz.getGenericSuperclass();
System.out.println( type.getActualTypeArguments()[0] );
You'd want to build something that was recursive that would check for this--maybe even go up the chain looking for java.util.List.

Categories