I am trying to use mirroring to call a method s1 of my class MyClass, with the parameter s2. Java is complaining that String.TYPE does not exist. I checked the API and it is right: I cannot do the same thing as when I call Integer.TYPE. But how can I solve the problem then? I need partype of type String, or else the method throws an exception.
public void trying(MyClass method, String s1, String s2){
try {
Class cls = Class.forName("MyClass");
Class partype[] = new Class[1];
partype[0] = String.TYPE;
Method meth = cls.getMethod(s1, partype);
meth.invoke(methobj, s2);
}
catch (Throwable e) {
System.err.println(e);
}
}
It's not a type, it's a class:
partype[0] = String.class;
Related
description:
I need use getMethod, it requires the parameterTypes.
The origin method requires double (a primitive type, not Double), and I can't change origin method.
I can't just input double.class in parameterTypes, because the s maybe diffierent types, such as Integer(not int).
The method parameter in Foo.java are always and only primitive types.
code:
test.java
public static void main( String args[] )
{
Object obj = new Foo();
Object s = 1.2;
String type = "Double";
try {
Method method = obj.getClass().getMethod("return" + type, s.getClass());// got NoSuchMethodException here, because it requires `double` not Double
System.out.println(method.invoke(obj,s));
} catch (NoSuchMethodException | IllegalAccessException |InvocationTargetException e) {
e.printStackTrace();
}
}
}
Foo.java //(I can't change/add code/delete in this part)
public class Foo {
public double returnDouble(double type){
return type;
}
public int returnInt(int type){
return type;
}
}
what I have tried:
Use Map
public static void main( String args[] )
{
Object obj = new Foo();
// Object s = 1;
// String type = "Int";
Object s = 1.2;
String type = "Double";
Map<String, Class> methodClassMap = new HashMap<String, Class>() {{
put("Double",double.class);
put("Integer",int.class);
}};
try {
Method method = obj.getClass().getMethod("return" + type, methodClassMap.get(s.getClass().getSimpleName()));
System.out.println(method.invoke(obj,s));
} catch (NoSuchMethodException | IllegalAccessException |InvocationTargetException e) {
e.printStackTrace();
}
}
}
It worked, but I have to list all possible type of value the s.
question:
Any better solution than using Map? Maybe use generic?
When you know beforehand that the target method always uses a primitive types, you can use the unwrap() method of MethodType of the java.lang.invoke package.
Object obj = new Foo();
Object s = 1.2;
String type = "Double";
try {
MethodType mt = MethodType.methodType(s.getClass(), s.getClass()).unwrap();
Method method = obj.getClass().getMethod("return" + type, mt.parameterArray());
System.out.println(method.invoke(obj, s));
} catch(ReflectiveOperationException e) {
e.printStackTrace();
}
Alternatively, when you’re already using the method type of the java.lang.invoke package, you can also use a method handle to perform the invocation.
Object obj = new Foo();
Object s = 1.2;
String type = "Double";
try {
MethodType mt = MethodType.methodType(s.getClass(), s.getClass()).unwrap();
MethodHandle mh = MethodHandles.lookup().bind(obj, "return" + type, mt);
System.out.println(mh.invoke(s));
} catch(Throwable e) {
e.printStackTrace();
}
But note that unlike Reflection, the return type has to be correctly specified for the lookup. I’m assuming the same return type as the parameter type, like in your example.
I want to call method of a reflection of okhttp3.Request. The method to be called is writeTo(okio.BufferedSink).
private String getBody(final Object body){
try {
final BufferedSink buffer = Okio.buffer((Sink) new Buffer());
Method methodWriteTo = findMethod(body.getClass(), "writeTo");
# Not working
methodWriteTo.invoke(body, BufferedSink.class.cast(buffer));
# Not working
methodWriteTo.invoke(body, (BufferedSink)buffer);
String body = buffer.buffer().readUtf8();
return body;
} catch (Exception e) {
...
}
return "";
}
I always got http3.RequestBody$Companion$toRequestBody$1.writeTo argument 1 has type okio.BufferedSink, got okio.RealBufferedSink.
The okio.BufferedSink is interface class, so how can invoke this method with okio.RealBufferedSink ?
public void etisLogAround(ProceedingJoinPoint joinPoint, EtisLog etisLog) throws Throwable {
Object[] args = joinPoint.getArgs();
MethodSignature methodSignature = (MethodSignature) joinPoint.getStaticPart().getSignature();
Method method = methodSignature.getMethod();
String[] paramNames = ((MethodSignature) joinPoint
.getSignature()).getParameterNames();
for(String paramName: paramNames) {
logger.info("paramName:" +paramName);
}
try {
Object result = joinPoint.proceed();
if(methodSignature instanceof MethodSignature) {
final Class<?>[] parameterTypes = methodSignature.getParameterTypes();
for(final Class<?> pt : parameterTypes){
logger.info("Parameter type:" + pt);
}
}
#SuppressWarnings("unchecked")
ResponseEntity<CaseOutlineHeader> returnValue = (ResponseEntity<CaseOutlineHeader>) result;
result = etisLog.trasactionDetail().toString()+" "+returnValue.getBody().getCode().toString();
} catch (IllegalArgumentException e) {
throw e;
}
}
The class CaseOutlineHeader is what I want to be changed. the parameterTypes variable contains the name of the class that I would like to pass inside the tag of the ResponseEntity<>. What if I would like to pass a different class Name. How should I do that to be flexible to accept the different class name?
If i do : ResponseEntity<parameterTypes> returnValue = (ResponseEntity<parameterTypes>) result;
it will say an error parameterTypes cannot be resolved to a type.
The problem is that your AOP method need to cast the result to something in order to get the code value it needs to log. That something must be known in advance, since you can't use type parameters in annotations, and therefore can't pass it to AOP methods. This means that all methods you access in AOP must come from a known interface, like this:
public interface LogCodeProvider {
String getLogCode();
}
public class CaseOutlineHeader implements LogCodeProvider {
#Override
public String getLogCode() {
return "My Code";
}
}
And then in your AOP method you can do like this:
#SuppressWarnings("unchecked")
ResponseEntity<LogCodeProvider> returnValue = ResponseEntity<LogCodeProvider>) result;
result = etisLog.trasactionDetail().toString()+" "+returnValue.getBody().getLogCode();
In my example I have implemented special method getLogCode() which returns a string, so each class can decide exactly what to output.
It does however look confusing to reuse the result variable to store the value returned from etisLog.trasactionDetail().
Below sample code ,
ResponseEntity<?> anyRandomMethod(){
if(any condition){
return new ResponseEntity<Animal>(new Animal(), httpstatus.OK);
}else{
return new ResponseEntity<SpaceShip>(new SpaceShip(), httpstatus.OK);
}
}
I have a method that expects a method reference:
expectsMethodRef(obj::someMethod);
I now only retrieve the method at compiletime with reflection. How do I get the method reference from a Method object?
Method method = obj.class.getMethod(methodNameStr);
expectsMethodRef(<how to input my method here?>);
You just need to use Method.invoke. Here is an example:
public class SomeObject{
public String someMethod(){
return "Test";
}
}
public String expectsMethodRef( Function<SomeObject, String> f ){
SomeObject so = new SomeObject();
return f.apply(so);
}
And here is how you invoke using plain lambda and Method object.
//plain lmbda
expectsMethodRef( SomeObject::someMethod );
//with method object
Method someMethod = SomeObject.class.getMethod("someMethod");
expectsMethodRef( (so) -> {
try {
return (String)someMethod.invoke(so);
} catch (Exception e) {
return null;
}
} );
The objective is simple, I want to create a method which load a class dynamically, access its method and passing their parameters value and getting the return value at run-time.
Class which will be called
class MyClass {
public String sayHello() {
return "Hello";
}
public String sayGoodbye() {
return "Goodbye";
}
public String saySomething(String word){
return word;
}
}
Main Class
public class Main {
public void loadClass() {
try {
Class myclass = Class.forName(getClassName());
//Use reflection to list methods and invoke them
Method[] methods = myclass.getMethods();
Object object = myclass.newInstance();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().startsWith("saySome")) {
String word = "hello world";
//**TODO CALL OBJECT METHOD AND PASS ITS PARAMETER**
} else if (methods[i].getName().startsWith("say")) {
//call method
System.out.println(methods[i].invoke(object));
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
private String getClassName() {
//Do appropriate stuff here to find out the classname
return "com.main.MyClass";
}
public static void main(String[] args) throws Exception {
new Main().loadClass();
}
}
My question is how to invoke method with parameters and passing its value? also getting the return value and its type.
I think you're just missing the fact that you can pass in arguments to invoke, as an Object[]:
Object result = methods[i].invoke(object, new Object[] { word });
Or using varargs, if you prefer:
Object result = methods[i].invoke(object, word);
(The above two calls are equivalent.)
See the documentation for Method.invoke for more details.
simply create the object of MyClass invoke the function like this
MyClass mc = new MyClass();
String word = "hello world";
String returnValue = mc.saySomething(word);
System.out.println(returnValue);//return hello world here
or do this
Class myclass = Class.forName(getClassName());
Method mth = myclass.getDeclaredMethod(methodName, params);
Object obj = myclass.newInstance();
String result = (String)mth.invoke(obj, args);
Try ::
Class c = Class.forName(className);
Method m = c.getDeclaredMethod(methodName, params);
Object i = c.newInstance();
String result = (String)m.invoke(i, args);