Class.getConstructor function parameters - java

I'm studying this piece of code and got stucked in the commented row:
protected <T> T creaOggetto(Class<T> classe, int id) {
try {
Package pacchetto = classe.getPackage();
String nomePacchetto = pacchetto.getName();
String nomeClasse = classe.getSimpleName();
String nomeClasseRisultato = nomePacchetto + ".impl." + nomeClasse + "Impl";
Class<?> classeRisultato = Class.forName(nomeClasseRisultato);
Constructor<?> costruttore = classeRisultato.getConstructor(new Class[] {int.class});
#SuppressWarnings("unchecked")
T risultato = (T)costruttore.newInstance(new Object[] {id});
return risultato;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
I know that getConstructor() returns the constructor object for that class but (new Class[] {int.class}) confuses me, what is his purpose?

According Java docs:
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
Returns a Constructor object that reflects the specified public
constructor of the class represented by this Class object. The
parameterTypes parameter is an array of Class objects that identify
the constructor's formal parameter types, in declared order. If this
Class object represents an inner class declared in a non-static
context, the formal parameter types include the explicit enclosing
instance as the first parameter.
So, classeRisultato.getConstructor(new Class[] {int.class}); returns the constructor that accepts one and only one int parameter or NoSuchMethodException if it doesn't exist.
In code you posted, note that, using that constructor, it creates a new instance of that class passing the id, that is the actual int argument:
T risultato = (T)costruttore.newInstance(new Object[] {id});

The getConstructor(new Class[]); method takes in an array of Classes that represent the parameters of the constructor you want to get.
For example, if you had a constructor in class X,
public X(int a, int b)
you could obtain said constructor using X.getClass().getConstructor(new Class[] {int.class, int.class});
The two int.class parameters in the array represent the two ints the the constructor take in.
Java Documentation

Since there are potentially multiple constructors, you need to specify the signature of the constructor to get the one you want when you call Class.getConstuctor.
The signature includes the type parameters to the constructor. The signature also includes the method name, but for a constructor the method name is always the class name in the source code, and it is always "<init>" in the compiled code, so you need not specify that.
So if you have new Class[] {int.class} then you are saying the signature consists of a single parameter of type "int". Which means you want the constructor X(int x) for class name X (note it is only the type of the parameters that matters, not the names of the parameter variables).
Also note that int.class resolves to java.lang.Integer.TYPE, which is used to denote the primitive type "int".

Related

Java reflection - Get actual type of T in a generic interface<T>

How do I get the actual type of T in a generic interface?
Given the following class:
class myClazz {
private final IGenericInterface<?> genericInterface;
myClazz(final IGenericInterface<otherClazz> genericInterface){
this.genericInterface = genericInterface;
}
}
How do I get the simple name of otherClazz out of the constructor using reflection?
I tried the following:
String otherClazzString = myClazz.class.getConstructors()[0].getParameterTypes()[0].toString(); //
but I do not know what to do next to obtain the simple name of the actual type used in the generic interface.
Close, but you need to use getGenericParameterTypes, as well as getDeclaredConstructors (since your constructor is not public):
Class<?> cls = (Class<?>) ((ParameterizedType) myClazz.class.getDeclaredConstructors()[0]
.getGenericParameterTypes()[0]) // first constructor, first parameter
.getActualTypeArguments()[0]; // first type argument
System.out.println(cls); // prints 'class otherClazz`
It should be noted that this code will only work if the type argument of the parameter is a concrete type, otherwise the cast to Class<?> will not work. For instance, in the case that the argument is a type variable, getActualTypeArguments()[0] will return an instance of TypeVariable instead of Class<...>.

Call getMethod with a subclass as method argument

Is it possible to invoke a method where the argument object or the argument class is a subclass and the method himself took the superclass as argument?
I trying to invoke this method public void setNewProblem(Problem problem); with a concrete implementation of the abstract class Problem. Unfortunately I get an NoSuchMethodException exception.
I call the invoke like this:
Method method = model.getClass().getMethod("set" + propertyName, new Class[] { newValue.getClass() });
method.invoke(model, newValue);
If I change newValue.getClass() to Problem.class everything works fine. Any idea how to pass a subclass to public void setNewProblem(Problem problem);?
You have to ask for the exact type it is. This is because you can have multiple possible overloaded methods and it needs to know exact what you wanted.
So you can invoke with a sub-class but you cannot ask for a sub-class without be being there.
What you can do is look at all methods and find a match.
If all you need is the setter or getter for a property, I suggest you look at BeanIntrospector which will find you all the properties and the getter/setter methods for that property.
The problem is that newValue.getClass() is a subclass of the class in the declared method.
From Class.getMethod:
To find a matching method in a class C: If C declares exactly one
public method with the specified name and exactly the same formal
parameter types, that is the method reflected.
You could work your way up the inheritance chain until it works:
Method getMethod(Class c1, Class c2) {
if(c2.getSuperClass() == null) {
return c1.getMethod("set" + propertyName, new Class[] { c2 });
}
try {
return c1.getMethod("set" + propertyName, new Class[] { c2 });
} catch(NoSuchMethodException e) {
return getMethod(c1, c2.getSuperClass());
}
}
Usage:
Method method = getMethod(model.getClass(), newValue.getClass());
I hesitate to suggest this, however, since it does not cover 100% of cases (such as if the formal argument class is an interface), and the way you are doing this is bad.
When you call Class.getMethod() you have to specify correctly the formal argument types. Not the types of the actual arguments you are planning to supply. You have to match precisely what it says in the declaration of the method concerned.
"The parameterTypes parameter is an array of Class objects that identify the method's formal parameter types, in declared order."

Why warning when trying to reflection on empty argument function

I was trying to use reflection to call funcA() of a class ClsA. However Eclipse Juno is showing a warning in TestA class remark with warning (A) as shown below.
The ClsA is like this:
public class ClsA {
String varA;
...
...
private String funcA() {
return varA;
}
}
This is the code I use the reflection call on funcA():
public class TestA {
public static void main(String[] args) {
ClsA clsA = new ClsA();
Class noparam[] = {};
Method funcA;
String retStr;
funcA = ClsA.class.getDeclaredMethod("funcA", noparam);
funcA.setAccessible(true);
retStr = (String) funcA.invoke(clsA, null); // warning (A)
}
}
And this is the warning I get. Basically I just don't really understand what is the message that warning trying to bring? How could I explicitly cast a null?
The argument of type null should explicitly be cast to Object[] for
the invocation of the varargs method invoke(Object, Object...) from
type Method. It could alternatively be cast to Object for a varargs
invocation
There can be 2 types of method invocation, one is to call with fixed argument, other is to call with variable arguments.
If you are providing just null as argument, it is not clear to java whether that method is for variable parameters or no parameter at all (as variable parameters may also accept no agrument).
So it asks to mention explicitly like (Object[])null or (Object)null, even if you don't want to provide any argument.
Invoking Methods
Since the method signature is (Object obj, Object... vars) unless you declare the null cast the most forward approach is to not include any arguments.
Example from your code above:
retStr = (String) funcA.invoke(clsA);
Anytime there are VarArgs (Something...) it means 0 or more.

How does a compiler infer type for a parameterless method?

From Effective Java :
One noteworthy feature of generic methods is that you needn’t specify
the value of the type parameter explicitly as you must when invoking
generic con- structors. The compiler figures out the value of the type
parameters by examining the types of the method arguments.
So how does the compiler infer type in case of a method that takes no parameter ?
For example consider the following static factory method that creates a new HashMap every time it is called :
// Generic static factory method
public static <K,V> HashMap<K,V> newHashMap() {
return new HashMap<K,V>();
}
And when the method is called like :
Map<String,String> pair = newHashMap(); //it returns a Map<String,String>
and when it called like
Map<String, List<String>> anagrams =newHashMap(); // it returns a Map<String,List<String>
It infers it based on the variable type that the return is assigned too.
public class GenericTest {
public static void main(final String[] args) {
final GenericTest test = new GenericTest();
String data = test.echo();
}
public <T> T echo() {
return null;
}
}
In code example above, the compiler infers the generic parameter type based on the type of the data field, in this case String.
The compiler has only a limited number of variables on which to infer types. If a method takes no arguments, then the method can only be a simple override, since return values cannot be used to type methods, that leaves the name of the method itself. The compiler has to choose how far up the inheritance chain to select which parent/child class has the method to actually be called.

How is the Java compiler able to distinguish between those two constructors/methods?

public class MyClass {
private String string;
private Object[] objects;
// constructor 1
public MyClass(String string, Object... objects) {
this.string = string;
this.objects = objects;
}
// constructor 2
public MyClass(String string) {
this.string = string;
}
public static void main(String[] args) {
MyClass myClass = new MyClass("foobar");
}
}
In that case, how did the Java compiler decide to use constructor 2 instead of constructor 1? Why no The constructor ... is ambiguous or a similar error occurs?
PS: the question works with classic methods too.
A var-args method/constructor will be chosen only if there is no non-var-arg method/constructor. So it is clear that why compiler chooses MyClass(String string).
It's always the most specific method that is called.
new MyClass("foobar");
searches to call that constructor which takes an object of type String as it's only argument.
and, var-args method will be used iff matching non-var-args method doesn't exist.
As I understand varargs constructors and methods are only syntax sugar, which transforms to array declarations. So your constructor 1 during compilation would be nearly equal to:
public MyClass(String string, Object[] objects) {
this.string = string;
this.objects = objects;
}
It means, that if you want to construct instance of MyClass by following code:
MyClass obj = new MyClass("Hello", "1", "2");
It would be equal to:
MyClass obj = new MyClass("Hello", new Object[]{"1", "2"} );
The JVM will look for Exact matching for passing values to variables in methods/constructor, if it's not able find exact match it will treat the values as Object.
jls-15.12.2 states that compiler will first look best match for without autoboxing or var agrs. Constructor #2 fits in your case.
If it had not been there, the first autoboxing will be applied, i.e. any method with parameter super class of String i.e. Object will be callled.
// constructor 2
public MyClass(Object string) {
this.string = string.toString();
}
Now, even after applying autoboxing, compiler is not able to find best match, it will go for var args. So, if you remove constructor 2 from your code, first constructor will be called.

Categories

Resources