I keep getting NoSuchMethodException in this line:
float modif = (float)sc.affectingObject.getClass().getMethod(sc.affectingMethodName, (Class<?>[])null).invoke(sc.affectingObject, (Object[])null);
where sc is an instance of class SubChange:
class SubChange implements Serializable {
String changeType;
Serializable affectingObject;
String affectingFieldName;
String affectingMethodName;
public SubChange(String chanType, Serializable affingObj, String affingFM) {
try{
changeType = chanType;
affectingObject = affingObj;
//deciding whether affingFM provides a field name or a method name
for (Field f : affingObj.getClass().getDeclaredFields()) {
if (f.getName().equals(affingFM) == true) {
affectingFieldName = affingFM;
break;
}
}
if (affectingFieldName == null) {
affectingMethodName = affingFM;
}
} catch(Exception e) {
e.printStackTrace();
}
}
//other class methods
}
whose instance has been initialized like this:
new SubChange("first", physio, "calcTotalFatigue")
where physio is an instance of inner class Physiology belonging to class Hm, while the SubChange constructor is being called from another inner class of the same Hm class.
Needless to say that method calcTotalFatigue() of physio exists.
Can anyone, please, suggest what I am doing wrong?
ANSWER
The error was caused by the wrong access modifier of the method calcTotalFatigue() which I naively omitted in the description of the problem. Making the method public solved the problem.
The exception is being thrown by this part:
sc.affectingObject.getClass().getMethod(
sc.affectingMethodName, (Class<?>[]) null)
and it means that the getMethod call cannot find the method that you requested.
I can't determine why from looking at your code, but I suggest that you print out the classname of the class being returned by sc.affectingObject.getClass(), print out the value of sc.affectingMethodName, and double check that the actual class (or a superclass) defines the method you are looking for.
Needless to say that method calcTotalFatigue() of physio exists.
Needless to say ... you might be wrong about that!
UPDATE
You asked:
Can it be something with access level modifiers?
The javadoc says:
"Returns a Method object that reflects the specified public member method of the class or interface represented by this Class object."
So ... yes, that could be the problem. If you want to retrieve non-public methods, then consider using getDeclaredMethod instead of getMethod.
Related
Consider the following class:
#ClassAnnotation1
#ClassAnnotation2
class MyClass {
// ...
#MethodAnnotation1
#MethodAnnotation2
private void myMethod(#ParamAnnotation1 #ParamAnnotation2 int i) {
// ...
}
}
During a reflection phase of my application, I need to analyze various code aspects, given a Method instance.
public void analyze(final Method method) {
// do the analysis...
// for example here, method is an instance of myMethod in MyClass
}
I can easily analyze the parameters' Annotation by doing
for (Parameter p : method.getParameters()) {
if (p.getAnnotation(ParamAnnotation1.class) != null) {
// ...
}
}
and get the results I expect.
The method's Annotation's can easily be processed with
method.getAnnotation(MethodAnnotation1.class)
Unfortunately I fail to get the expected results for the class' Annotation's.
In fact, the call to
method.getClass().getAnnotation(ClassAnnotation1.class)`
returns null, whereas MyClass is clearly annotated by #ClassAnnotation1.
How do I get the MyClass annotations from a Method instance?
You have to use method.getDeclaringClass().getAnnotation(ClassAnnotation1.class)
The fact that method.getParameters() returns the method's parameters, probably mislead you into thinking that method.getClass() returns the class containing your method.
Method::getClass() in fact returns Class<? extends Method>, which is clearly not annotated by #ClassAnnotation1. That's why you got null
I have a list called itemsData of object of class EtcStruct, but the class can differ depending on the file i want to use (the class is full of variables setters and getters):
ObservableList<EtcStruct> itemsData = FXCollections.observableArrayList();
Im passing it to the method thats supposed to work for any object type i choose and run invoked method from the file.
public static void parseToFile(ObservableList itemsData){
EtcStruct itemObject = (EtcStruct) itemsData.get(0);
System.out.print((int)reflectedmethod.invoke(itemObject);
}
Code above works , but what i want to achieve is make the method work without editing it's object type to make it more flexible for whatever structclass i plan to use.
I tried something with passing Struct Class name and .getClass() it returns the original type but i dont know what to do with it to make the new object of itemsData original type and cast the itemsData object.
public static void parseToFile(ObservableList itemsData,Class c){
Object itemObject = c.newInstance();
Object newobject = curClass.newInstance();
newobject = c.cast(itemsList.get(0));
}
Above seemed dumb to me and obviously didnt work.
After reading your comment I understand better why one would use reflection in your case. A GUI builder/editor is an example where reflection is used to provide an interface to set/get the values of components. Still, IMHO, reflection isn't a tool you would design for when you own the classes and are the primary designer. If possible you should strive for something more like this:
interface Parsable {
default int parse() {
System.out.println("Here I do something basic");
return 0;
}
}
class BasicStruct implements Parsable { }
class EtcStruct implements Parsable {
#Override
public int parse() {
System.out.println("Here I do something specific to an EtcStruct");
return 1;
}
}
// If some structs have a parent-child relationship
// you can alternatively `extend EtcStruct` for example.
class OtherStruct extends EtcStruct {
#Override
public int parse() {
super.parse();
System.out.println("Here I do something specific to an OtherStruct");
return 2;
}
}
void parseToFile(Parsable parsable) {
System.out.println(parsable.parse());
}
// If you use a generic with a specific class you don't
// have to guess or care which kind it is!
void parseToFile(ObservableList<Parsable> parsables) {
for (Parsable p : parsables) {
parseToFile(p);
}
}
public static void main(String[] args) {
ObservableList<Parsable> parsables = FXCollections.observableArrayList();
parsables.add(new BasicStruct());
parsables.add(new EtcStruct());
parsables.add(new OtherStruct());
parseToFile(parsables);
}
Output:
Here I do something basic
0
Here I do something specific to an EtcStruct
1
Here I do something specific to an EtcStruct
Here I do something specific to an OtherStruct
2
Of course, this is just an example that needs to be altered to meet your needs.
But what I still don't get is if you're able to parse from a file why you can't parse to one. Nonetheless, I slapped some code together to show you how I might parse an object to a file, manually, when dealing with Objects only.
The idea is to satisfy a bean-like contract. That is, each structure should provide a parameter-less constructor, all fields you want managed by reflection will follow Java naming convention and will have both a public setter and getter.
Don't get caught up in the file writing; that will be determined by your needs. Just notice that by following this convention I can treat any Object as a parsable structure. A less refined version here for reference:
public void parseToFile(Object object) throws IOException, InvocationTargetException, IllegalAccessException {
fos = new FileOutputStream("example" + object.getClass().getSimpleName());
List<Method> getters = Arrays.stream(object.getClass().getMethods())
.filter(method -> method.getName().startsWith("get") && !method.getName().endsWith("Class"))
.collect(Collectors.toList());
for (Method getter : getters) {
String methodName = getter.getName();
String key = String.valueOf(Character.toLowerCase(methodName.charAt(3))) +
methodName.substring(4, methodName.length());
fos.write((key + " : " + String.valueOf(getter.invoke(object)) + "\n").getBytes());
}
fos.close();
}
I think that you can just still use Generics to keep static objects typing. Try to parametrize your function parseToFile. Here is an example:
public static void parseToFile(ObservableList<EtcStruct> itemsData){
EtcStruct itemObject = itemsData.get(0);
System.out.print((int)reflectedmethod.invoke(itemObject);
}
I have this code:
Class I want to copy:
public class NormalChair extends AbstractChair {
protected int height;
protected String name;
public NormalChair() {
super();
}
public NormalChair(String name, int height) {
super(name, height);
}
// Copy constructor - getName() and getHeight() are defined in parent class.
public NormalChair(NormalChair chair) {
this(chair.getName(), chair.getHeight());
}
}
Create some class
public Object createObj(String cls_name, String param1, int param2){
return Class.forName(cls_name).getConstructor(String.class, Integer.class).newInstance(param1, param2);
}
Then I try to copy object of that class using this:
Object obj_to_copy = createObj("Classname", "name", 10);
String cls_name = obj_to_copy.getClass().getName();
Class.forName(cls_name).getConstructor(Object.class).newInstance(obj_to_copy);
And I get this error:
Exception in thread "main" java.lang.NoSuchMethodException: test.NormalChair.<init>(java.lang.Object)
at java.lang.Class.getConstructor0(Class.java:2800)
at java.lang.Class.getConstructor(Class.java:1708)
at test.ProductTrader.create(ProductTrader.java:57)
at test.Test.main(Test.java:23)
So I suppose I need to call copy constructor somehow differently than showing it's type as Object?
P.S. Also I gave this example as simplistic. But in reality I would not know which class needs to be copied before runtime, so using copy constructor should not depend only on NormalChair class.
Update:
I updated my question, to make it more clear that when I copy object, before, runtime, I won't know what class it will need to copy.
Java reflection for some reason matches classes and method signatures strictly. So in order to find a matching constructor, you would need to enumerate available constructors with Class.getDeclaredConstructors() and find a matching one.
I have written a small library to simplify the task, here is a method matching class from it: HavingMethodSignature.
If you're interested, here is how you create a new instance with this lib:
Object o = OpenBean.newInstance(Class.forName(cls_name));
Why bother with a copy constructor in Java ? There is a standard way to copy an object in Java : simply clone it. If default cloning is not relevant, override the clone() method.
You simply need to write obj.clone() to get a copy.
see Java documentation of clone() for details.
If you can assume that a copy constructor accepts an object of the same class, you can do something like:
class ObjectCopier {
public static Object copy(Object orig) {
Class<?> cls = orig.getClass();
Constructor<?> con = cls.getDeclaredConstructor(cls);
return ((con == null) ? null : con.newInstance(orig);
}
}
(untested, so treat it as such)
I know that I can dynamically determine the return type of a method by passing in a class and then declaring that class as the return type, as in the following example:
public static <returnType> returnClass dynamicConstructor (Class<returnType> returnClass){
//Dynamically construct an object using a zero parameter constructor
return (returnClass) object;
}
The code that would be called would be:
DynamicConstructor.construct(constructedClass);
This code is nice because when you're calling the method in code, it's simple and encapsulated. However, I would like to take it one step further and make the parameter a string containing the full class name, then have the method determine the type of the object via Class.forName(className). The trouble with this is that I can't figure out how to return the type as determined later on. Using method overloading, the code I have is:
public static ?????? dynamicConstructor (String className){
try {
return dynamicConstructor(Class.forName(className));
} catch (ClassNotFoundException e) {
System.out.println("Could not find class at path " + classPath);
e.printStackTrace();
return null;
}
}
The problem is that I have no idea what to put in the part labeled ??????. I want to be able to somehow reference the class determined by Class.forName(className). Is this possible?
It's certainly possible to create an instance of the class from the class name (as long as you have a zero-arg constructor), but if the only place the class name is stored is in a String, then there's no way to determine the type to return. So your method signature would be
public static Object dynamicConstructor(String)
which is probably not very useful or what you want.
So the short answer is, no, this is not possible.
How can I find out through reflection what is the string name of the method?
For example given:
class Car{
public void getFoo(){
}
}
I want to get the string "getFoo", something like the following:
Car.getFoo.toString() == "getFoo" // TRUE
You can get the String like this:
Car.class.getDeclaredMethods()[0].getName();
This is for the case of a single method in your class. If you want to iterate through all the declared methods, you'll have to iterate through the array returned by Car.class.getDeclaredMethods():
for (Method method : Car.class.getDeclaredMethods()) {
String name = method.getName();
}
You should use getDeclaredMethods() if you want to view all of them, getMethods() will return only public methods.
And finally, if you want to see the name of the method, which is executing at the moment, you should use this code:
Thread.currentThread().getStackTrace()[1].getMethodName();
This will get a stack trace for the current thread and return the name of the method on its top.
Since methods aren't objects themselves, they don't have direct properties (like you would expect with first-class functions in languages like JavaScript).
The closest you can do is call Car.class.getMethods()
Car.class is a Class object which you can use to invoke any of the reflection methods.
However, as far as I know, a method is not able to identify itself.
So, you want to get the name of the currently executing method? Here's a somewhat ugly way to do that:
Exception e = new Exception();
e.fillInStackTrace();
String methodName = e.getStackTrace()[0].getMethodName();
Look into this thread:
Getting the name of the currently executing method
It offers some more solutions - for example:
String name = new Object(){}.getClass().getEnclosingMethod().getName();
With Java 8, you can do this with a few lines of code (almost) without any additional libraries. The key is to convert your method into a serialisable lambda expression. Therefore, you can just define a simple interface like this:
#FunctionalInterface
public interface SerializableFunction<I, O> extends Function<I, O>, Serializable {
// Combined interface for Function and Serializable
}
Now, we need to convert our lambda expression into a SerializedLambda object. Apparently, Oracle does not really want us to do that, so take this with a grain of salt... As the required method is private, we need to invoke it using reflections:
private static final <T> String nameOf(SerializableFunction<T, ?> lambda) {
Method findMethod = ReflectionUtils.findMethod(lambda.getClass(), "writeReplace");
findMethod.setAccessible(true);
SerializedLambda invokeMethod = (SerializedLambda) ReflectionUtils.invokeMethod(findMethod, lambda);
return invokeMethod.getImplMethodName();
}
I'm using Springs ReflectionUtils class here for simplicity, but you can of course replace this by manually looping through all superclasses and use getDeclaredMethod to find the writeReplace method.
And this is it already, now you can use it like this:
#Test
public void testNameOf() throws Throwable {
assertEquals("getName", nameOf(MyClassTest::getName));
}
I haven't checked this with Java 9s module system, so as a little disclaimer it might be more tricky to do this with more recent Java versions...
try this,
import java.lang.reflect.*;
public class DumpMethods {
public static void main(String args[]) {
try {
Class c = Class.forName(args[0]);
Method m[] = c.getDeclaredMethods();
for (int i = 0; i < m.length; i++)
System.out.println(m[i].toString());
} catch (Throwable e) {
System.err.println(e);
}
}
}
Wait, since you already know the method name, can't you just type it as a string?
Instead of (pseudo) Class.methodName.toString(), just use "methodName".
Otherwise you can use Class#getDeclaredMethods() to get all the methods in a class