I want to create a Mock Library class that implements InvocationHandler interface from Java Reflection.
This is the template I have created:
import java.lang.reflect.*;
import java.util.*;
class MyMock implements InvocationHandler {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// todo
}
public MyMock when(String method, Object[] args) {
// todo
}
public void thenReturn(Object val) {
// todo
}
}
The when and thenReturn methods are chained methods.
Then when method registers the given mock parameters.
thenReturn method registers the expected return values for the given mock parameters.
Also, I want to throw java.lang.IllegalArgumentException if the proxied interface calls methods or uses parameters that are not registered.
This is a sample interface:
interface CalcInterface {
int add(int a, int b);
String add(String a, String b);
String getValue();
}
Here we have two overloaded add methods.
This is a program to test the mock class I wanted to implement.
class TestApplication {
public static void main(String[] args) {
MyMock m = new MyMock();
CalcInterface ref = (CalcInterface) Proxy.newProxyInstance(MyMock.class.getClassLoader(), new Class[]{CalcInterface.class}, m);
m.when("add", new Object[]{1,2}).thenReturn(3);
m.when("add", new Object[]{"x","y"}).thenReturn("xy");
System.out.println(ref.add(1,2)); // prints 3
System.out.println(ref.add("x","y")); // prints "xy"
}
}
This is the code which I have implemented so far to check the methods in CalcInterface:
class MyMock implements InvocationHandler {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
int n = args.length;
if(n == 2 && method.getName().equals("add")) {
Object o1 = args[0], o2 = args[1];
if((o1 instanceof String) && (o2 instanceof String)) {
String s1 = (String) o1, s2 = (String) o2;
return s1+ s2;
} else if((o1 instanceof Integer) && (o2 instanceof Integer)) {
int s1 = (Integer) o1, s2 = (Integer) o2;
return s1+ s2;
}
}
throw new IllegalArgumentException();
}
public MyMock when(String method, Object[] args) {
return this;
}
public void thenReturn(Object val) {
}
}
Here I am checking only for methods with the name add and having 2 arguments, with their type as String or Integer.
But I wanted to create this MyMock class in a general fashion, supporting different interfaces not just CalcInterface, and also supporting different methods not just the add method I implemented here.
You have to separate the builder logic from the object to build. The method when has to return something which remembers the arguments, so that the invocation of thenReturn still knows the context.
For example
public class MyMock implements InvocationHandler {
record Key(String name, List<?> arguments) {
Key { // stream().toList() creates an immutable list allowing null
arguments = arguments.stream().toList();
}
Key(String name, Object... arg) {
this(name, arg == null? List.of(): Arrays.stream(arg).toList());
}
}
final Map<Key, Function<Object[], Object>> rules = new HashMap<>();
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
var rule = rules.get(new Key(method.getName(), args));
if(rule == null) throw new IllegalStateException("No matching rule");
return rule.apply(args);
}
public record Rule(MyMock mock, Key key) {
public void thenReturn(Object val) {
var existing = mock.rules.putIfAbsent(key, arg -> val);
if(existing != null) throw new IllegalStateException("Rule already exist");
}
public void then(Function<Object[], Object> f) {
var existing = mock.rules.putIfAbsent(key, Objects.requireNonNull(f));
if(existing != null) throw new IllegalStateException("Rule already exist");
}
}
public Rule when(String method, Object... args) {
Key key = new Key(method, args);
if(rules.containsKey(key)) throw new IllegalStateException("Rule already exist");
return new Rule(this, key);
}
}
This is already capable of executing your example literally, but also supports something like
MyMock m = new MyMock();
CalcInterface ref = (CalcInterface) Proxy.newProxyInstance(
CalcInterface.class.getClassLoader(), new Class[]{CalcInterface.class}, m);
m.when("add", 1,2).thenReturn(3);
m.when("add", "x","y").thenReturn("xy");
AtomicInteger count = new AtomicInteger();
m.when("getValue").then(arg -> "getValue invoked " + count.incrementAndGet() + " times");
System.out.println(ref.add(1,2)); // prints 3
System.out.println(ref.add("x","y")); // prints "xy"
System.out.println(ref.getValue()); // prints getValue invoked 1 times
System.out.println(ref.getValue()); // prints getValue invoked 2 times
Note that when you want to add support for rules beyond simple value matching, a hash lookup will not work anymore. In that case you have to resort to a data structure you have to search linearly for a match.
The example above uses newer Java features like record classes but it shouldn’t be too hard to rewrite it for previous Java versions if required.
It’s also possible to redesign this code to use the real builder pattern, i.e. to use a builder to describe the configuration prior to creating the actual handler/mock instance. This allows the handler/mock to use an immutable state:
public class MyMock2 {
public static Builder builder() {
return new Builder();
}
public interface Rule {
Builder thenReturn(Object val);
Builder then(Function<Object[], Object> f);
}
public static class Builder {
final Map<Key, Function<Object[], Object>> rules = new HashMap<>();
public Rule when(String method, Object... args) {
Key key = new Key(method, args);
if(rules.containsKey(key))
throw new IllegalStateException("Rule already exist");
return new RuleImpl(this, key);
}
public <T> T build(Class<T> type) {
Map<Key, Function<Object[], Object>> rules = Map.copyOf(this.rules);
return type.cast(Proxy.newProxyInstance(type.getClassLoader(),
new Class[]{ type }, (proxy, method, args) -> {
var rule = rules.get(new Key(method.getName(), args));
if(rule == null) throw new IllegalStateException("No matching rule");
return rule.apply(args);
}));
}
}
record RuleImpl(MyMock2.Builder builder, Key key) implements Rule {
public Builder thenReturn(Object val) {
var existing = builder.rules.putIfAbsent(key, arg -> val);
if(existing != null) throw new IllegalStateException("Rule already exist");
return builder;
}
public Builder then(Function<Object[], Object> f) {
var existing = builder.rules.putIfAbsent(key, Objects.requireNonNull(f));
if(existing != null) throw new IllegalStateException("Rule already exist");
return builder;
}
}
record Key(String name, List<?> arguments) {
Key { // stream().toList() createns an immutable list allowing null
arguments = arguments.stream().toList();
}
Key(String name, Object... arg) {
this(name, arg == null? List.of(): Arrays.stream(arg).toList());
}
}
}
which can be used like
AtomicInteger count = new AtomicInteger();
CalcInterface ref = MyMock2.builder()
.when("add", 1,2).thenReturn(3)
.when("add", "x","y").thenReturn("xy")
.when("getValue")
.then(arg -> "getValue invoked " + count.incrementAndGet() + " times")
.build(CalcInterface.class);
System.out.println(ref.add(1,2)); // prints 3
System.out.println(ref.add("x","y")); // prints "xy"
System.out.println(ref.getValue()); // prints getValue invoked 1 times
System.out.println(ref.getValue()); // prints getValue invoked 2 times
InvocationHandler is used to invoke a method signature against an object instance (proxy) that's instantiated in memory (or a class, if you're tapping a static method), not to support methods that aren't in the supplied proxy's class at all, which is how you've implemented it here. That said, you can probably achieve what you're trying to do by holding the method signature you're trying to mock (as well as the value you want to return when the args match) in private variables.
This may work, with the proviso that I haven't done Java in a couple of years, so I may be a bit rusty:
class MyMock implements InvocationHandler {
private String methodName = null;
private Object[] supportedArgs = null;
private Object returnValue = null;
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// If we don't know when this mock is supposed to be used, it's useless
assert this.methodName != null: "when(method, args) hasn't been called against the mock yet!";
// Note that both args and supportedArgs will be null if the method signature has no params
if (method.getName().equals(this.methodName) && this.supportedArgs == args) {
return this.returnValue;
}
try {
return method.invoke(proxy, args);
}
catch (IllegalAccessException | InvocationTargetException innerException){
// The proxy didn't support the method either, so let's throw an IllegalArgumentException
throw new IllegalArgumentException("The supplied method signature isn't implemented in the proxy.");
}
}
public MyMock when(String method, Object[] args) {
this.methodName = method;
this.supportedArgs = args;
return this;
}
public void thenReturn(Object val) {
this.returnValue = val;
}
}
Related
i have an issue on verifying a call to method of class under test, using the verify() method it tells that the call is not done to that method, this method is defined as abstract in super class (loadFile(String))
find bellow the code :
public abstract class FileParser {
public Iterator<String> loadFile(FileSettingsToSend fileSetting) {
System.out.println("file before staged");
try {
if(!movFile("staged",fileSetting))
return null;
System.out.println("file after move "+fileSetting.getFile().getAbsolutePath());
boolean isValidFormatFile = fileValidator.checkFileFormat(fileSetting);
if (!isValidFormatFile) {
System.out.println("file format is not valid");
return null;
}
return readBlock(fileSetting);
} catch (Exception e) {
System.out.println(e.getMessage());
return null;
} finally {
}
//return null;
}
public abstract Iterator<String> readBlock(FileSettingsToSend fileSettingsToSend)
throws JsonProcessingException, IOException;
}
public class JsonFileParser extends FileParser {
public final ObjectMapper mapper = new ObjectMapper();
#Autowired
public JsonFileParser(FileValidator jsonFileValidatorService, FileAttributeService fileAttributeService) {
super(jsonFileValidatorService, fileAttributeService);
}
#Override
public Iterator<String> readBlock(FileSettingsToSend fileSetting) throws JsonProcessingException, IOException {
ObjectMapper mapper = new ObjectMapper();
System.out.println("inside readBlock json implementation");
List<String> listAttribute = fileAttributeService.getAttributes(fileSetting.getDiretoryPath());
String[] blocDelimitor = fileAttributeService.getDelimitorRepositpry(fileSetting.getDiretoryPath());
System.out.println("after validator");
final JsonNode root = mapper.readTree(fileSetting.getFile());
if (root == null)
return null;
Iterator<JsonNode> nodeIterator = root.elements();
System.out.println("Data is " + root);
return new Iterator<String>() {
JsonNode node;
#Override
public boolean hasNext() {
return nodeIterator.hasNext();
}
#Override
public String next() {
int i = 0;
node = nodeIterator.next();
System.out.println("after nex " + node.toString());
Arrays.stream(blocDelimitor).forEach(e -> {
node = node.path(e);
System.out.println("inside next " + node.toString());
});
String result = null;
if (node.isArray()) {
System.out.println("It is Array");
for (JsonNode node1 : node) {
if (i != 0)
result = result + "," + listAttribute.stream().map(e -> e + "=" + node1.get(e))
.collect(Collectors.joining(","));
else
result = listAttribute.stream().map(e -> e + "=" + node1.get(e))
.collect(Collectors.joining(","));
i++;
}
} else
result = listAttribute.stream().map(e -> e + "=" + node.get(e)).collect(Collectors.joining(","));
return result;
}
};
}
Test method is :
#Mock
FileValidator jsonFileValidatorService;
#Mock
FileAttributeService fileAttributeService;
JsonFileParser jsonFileParserMock = new JsonFileParser(jsonFileValidatorService, fileAttributeService);
#Test
public void validatorNotTrue() throws JsonProcessingException, IOException{
when(jsonFileValidatorService.checkFileFormat( anyObject() )).thenReturn(true);
JsonFileParser jsonFileParser = Mockito.spy(jsonFileParserMock);
doReturn(true).when(jsonFileParser).movFile(anyString(),anyObject() );
assertNull(jsonFileParser.loadFile(null));
verify(jsonFileParser, times(1)).movFile(anyString(),anyObject());
assertTrue(jsonFileParser.movFile(anyString(), anyObject()));
assertTrue(jsonFileValidatorService.checkFileFormat( anyObject() ));
//exception.expect(Exception.class);
verify(jsonFileParser,times(1)).readBlock(anyObject();
}
#BeforeClass
public static void settingUp(){
}
#Before
public void initMock(){
MockitoAnnotations.initMocks(this);
}
the line verify(jsonFileParser,times(1)).readBlock(anyObject(); return false; meaning that the method loadFile of jsonfileParser not called
can you get your held to tell why it is not called.
Thank you.
This happens because you initialize the mocks after you create a JsonFileParser. Note that #Before method is executed after all the fields of your test class are initialized.
As a result, you pass null dependencies to the class. The invocation to the null FileValidator throws NullPointerException, but you swallow it in your catch block.
Generally it is advisable to verify the arguments you pass to your constructors and methods, to fail fast in case of an error. For example, Java comes with a handy Objects::requireNonNull method to verify that the passed parameters are non-null.
Similarly it's generally a bad practice to swallow every single exception. For instance, in your example, you expect IOException and JsonProcessingException to be thrown. It's better to catch these explicitly and let the program crash (or at least log a warning) for any other one.
Finally, mocks and spies are prone to overuse. Usually, it's enough to use fakes - dummy implementations of your interfaces. Depending on how much control you have over the code, you may also want to refactor it to avoid using a spy at all. Using one in a code you may freely change may signal an architectural problem.
I have a legacy project with Spring version 3.0 (I can't use Retriable annotation from spring package).
I want to implement Retryable annotation to annotate my methods which execution should be retry on fail.
This is my class:
#Component
public final class RetryBpp implements BeanPostProcessor {
private final ConcurrentMap<String, ClassDefinition> map = new ConcurrentHashMap<>();
#Override
public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
final Class<?> asClass = bean.getClass();
final Method[] methods = asClass.getMethods();
final List<Method> collect = Stream.of(methods)
.filter(method -> method.isAnnotationPresent(Repitable.class))
.collect(Collectors.toList());
if(!collect.isEmpty()){
this.map.put(beanName,new ClassDefinition(collect,asClass));
}
return bean;
}
#Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
final ClassDefinition definition = this.map.get(beanName);
if(definition != null){
final Class beanClass = definition.asClass;
return Proxy.newProxyInstance(beanClass.getClassLoader(), beanClass.getInterfaces(), (proxy, method, args) -> {
if(definition.isMethodPresent(method)){
System.out.println("Present");
return this.retry(definition.originalMethod(method),bean,args);
} else{
return method.invoke(bean,args);
}
});
} else{
return bean;
}
}
private Object retry(final Method method,final Object originalBean,Object[] argc){
final Repitable repitable = method.getAnnotation(Repitable.class);
int attempts = repitable.attempts();
Throwable exc = null;
while(attempts!=0){
try{
return method.invoke(originalBean,argc);
}catch (final Throwable throwable){
exc = throwable;
attempts--;
this.sleep(repitable.delay(),repitable.timeUnit());
}
}
throw new RuntimeException(exc);
}
#SneakyThrows(InterruptedException.class)
private void sleep(final int time, final TimeUnit timeUnit){
timeUnit.sleep(time);
}
#AllArgsConstructor
private static final class ClassDefinition{
private final List<Method> methods;
private final Class asClass;
boolean isMethodPresent(final Method method){
return this.methods.stream().anyMatch(mthd->mthd.getName().equals(method.getName()));
}
Method originalMethod(final Method method){
return this.methods.stream().filter(mthd->mthd.getName().equals(method.getName())).findFirst().orElseThrow(NullPointerException::new);
}
}
}
And it work but I want to change two things
1)In retry method I want to keep last exception and throw when repeateCount = 0 for this I need to declare null ptr to exc, but I want all my fields to be final. Is it any possible way to rewrite my code?
2) In ClassDefinition I compare Method by name because original equals method of Method class compare by class, I can't do it because original class replaced by proxy, Is it possible to compare two Method's in different way?
For the first part: one option is to use a List of the Throwables. And then throw them something like:
final RuntimeException theError = new RuntimeException(list.get(list.size() - 1));
for (int i = 0; i < list.size() - 1; i++) {
theError.addSuppressed(list.get(i));
}
throw theError;
This also gives the benefit of providing all the failures.
For the latter part, this could be complicated and expensive depending on whether there's gonna be generics, var-args, etc. on some of the methods.
Since you're already using spring, you could try a combination of: AopUtils.getTargetClass and AopUtils.getMostSpecificMethod to get what you want (would need to refactor what you pass to some of the test methods).
Something simple to provide some more (note, not infallible) tests in the vein of what you're already running with though:
return methods.stream().anyMatch(mthd -> {
if (!method.getName().equals(mthd.getName())) {
return false;
}
if (!method.getReturnType().isAssignableFrom(mthd.getReturnType())) {
return false;
}
final Class<?>[] parameterTypes = method.getParameterTypes();
if (mthd.getParameterTypes().length != parameterTypes.length) {
return false;
}
for (int i = 0; i < parameterTypes.length; i++) {
if (!parameterTypes[i].equals(mthd.getParameterTypes()[i])) {
return false;
}
}
return true;
});
I'm sure there are some more checks you can do (Method.getExceptionTypes() to confirm they could override; Method.getDeclaringClass() and work up the tree from there; Method.getModifiers() to determine if it could be overridden, etc.), but that depends on how safe you need to be.
Consider a field weight in class Animal. I want to be able to create a getter and setter functional interface objects for manipulating this field.
class Animal {
int weight;
}
My current approach is similar to one used for methods:
public static Supplier getter(Object obj, Class<?> cls, Field f) throws Exception {
boolean isstatic = Modifier.isStatic(f.getModifiers());
MethodType sSig = MethodType.methodType(f.getType());
Class<?> dCls = Supplier.class;
MethodType dSig = MethodType.methodType(Object.class);
String dMthd = "get";
MethodType dType = isstatic? MethodType.methodType(dCls) : MethodType.methodType(dCls, cls);
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle fctry = LambdaMetafactory.metafactory(lookup, dMthd, dType, dSig, lookup.unreflectGetter(f), sSig).getTarget();
fctry = !isstatic && obj!=null? fctry.bindTo(obj) : fctry;
return (Supplier)fctry.invoke();
}
But this gives the following error:
java.lang.invoke.LambdaConversionException: Unsupported MethodHandle kind: getField x.Animal.weight:()int
UPDATE
I am trying to create a class ObjectMap implementing interface Map, which basically tries to represent an object as a Map, where the object can be of any type. Was currently using Field.get() and Field.set() for manipulating fields in get() and put() methods, and using above mentioned approach to create Supplier and Consumer objects for invoking getter and setter methods. I was wondering if i could merge the two separate methods into one.
Example class which could be used as a Map through ObjectMap:
public class ThisCanBeAnything {
/* fields */
public String normalField;
private int hiddenFiled;
private String hiddenReadonlyField;
/* getters and setters */
public int hiddenField() {
return hiddenField;
}
public void hiddenField(int v) {
System.out.println("set: hiddenField="+v);
hiddenField = v;
}
public String hiddenReadonlyField() {
return hiddenReadonlyField;
}
}
And here is the expected usage:
Object o = new ThisCanBeAnything();
Map m = new ObjectMap(o);
m.put("normalField", "Normal");
System.out.println(m.get("normalField")); // Normal
m.put("hiddenField", 1); // set: hiddenField=1
System.out.println(m.get("hiddenField")); // 1
m.put("hiddenReadonlyField", 1); // does not do anything
System.out.println(m.get("hiddenReadonlyField")); // null
You are making it too difficult that it needs to be. When you have a Field, you can directly invoke unreflectGetter on the lookup factory to retrieve a MethodHandle:
Produces a method handle giving read access to a reflected field. The type of the method handle will have a return type of the field's value type. If the field is static, the method handle will take no arguments. Otherwise, its single argument will be the instance containing the field.
public static Supplier<Object> getter(Object obj, Class<?> cls, Field f) {
f.setAccessible(true);
MethodHandles.Lookup lookup = MethodHandles.lookup();
return () -> {
try {
MethodHandle handle = lookup.unreflectGetter(f);
return Modifier.isStatic(f.getModifiers()) ? handle.invoke() : handle.invoke(obj);
} catch (Throwable t) {
throw new IllegalArgumentException(t);
}
};
}
This returns a supplier of the value of the field. Depending on the accessibility of the field, you might need to invoke setAccessible(true).
Note that method handles and the reflection API also differs in terms of performance and might be faster.
Functional style lets you think about such things in new ways. Instead of a reflection-based approach like
Supplier getter(Object obj, Class<?> cls, Field f){...}
try something like
static <O,F> Supplier<F> getter(O obj, Function<O,F> extractor) {
return () -> extractor.apply(obj);
}
which you would invoke like
Supplier<Integer> getWeight = getter(animal, a -> a.weight);
Integer weight = getWeight.get();
Is a -> a.weight any harder than coming up with a Field via reflection?
One advantage is that you could use fields or methods as needed, e.g., if you added a getter for weight,
Supplier<Integer> getWeight = getter(animal, Animal::getWeight);
A similar setter factory might be
static <O,F> Consumer<F> setter(O obj, BiConsumer<O,F> modifier) {
return field -> modifier.accept(obj,field);
}
Invoked like this
Consumer<Integer> setWeight = setter(animal, (a, w) -> a.weight = w);
setWeight.accept(90);
You can directly write the lambda, you don't need the LambdaMetafactory at all:
public static Supplier getter(Object obj, Field f) {
return () -> {
try {
return f.get(obj);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
};
}
Or a runtime-typesafe version:
public static <T> Supplier<T> getter(Object obj, Class<T> fieldClass, Field f) {
if (!fieldClass.isAssignableFrom(f.getType()))
throw new RuntimeException("Field is not of expected type");
return () -> {
try {
return (T) f.get(obj);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
};
}
e.g.:
private class X {
public int a;
}
#Test
public void supplier_getter_test() throws NoSuchFieldException {
X a = new X();
a.a = 5;
Supplier<Integer> sup = getter(a, int.class, X.class.getField("a"));
assertEquals(5, sup.get().intValue());
}
You can’t bind a MethodHandle bearing a direct access to a field to a function interface instance, but you can bind the accessor method of the Field instance:
public static Supplier getter(Object obj, Class<?> cls, Field f) throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle get=lookup.findVirtual(Field.class,"get",MethodType.genericMethodType(1));
MethodHandle fctry = LambdaMetafactory.metafactory(lookup, "get",
get.type().changeReturnType(Supplier.class), MethodType.genericMethodType(0),
get, MethodType.genericMethodType(0)).getTarget();
return (Supplier)fctry.invoke(f, Modifier.isStatic(f.getModifiers())? null: obj);
}
Though in this specific example you may consider generating an IntSupplier instead:
public static IntSupplier getter(Object obj, Class<?> cls, Field f) throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle get=lookup.findVirtual(Field.class, "getInt",
MethodType.methodType(int.class, Object.class));
MethodHandle fctry = LambdaMetafactory.metafactory(lookup, "getAsInt",
get.type().changeReturnType(IntSupplier.class), MethodType.methodType(int.class),
get, MethodType.methodType(int.class)).getTarget();
return (IntSupplier)fctry.invoke(f, Modifier.isStatic(f.getModifiers())? null: obj);
}
…
final Animal animal = new Animal();
IntSupplier s=getter(animal, Animal.class, Animal.class.getDeclaredField("weight"));
animal.weight=42;
System.out.println(s.getAsInt());
I know it's a late answer, but I have developed a library that you can use to turn any MethodHandle into a lambda function. The performance is the same as if you would manually implement the function with direct access.
The impl is based around the fact that static final MethodHandles are being inlined to point of being as fast as direct access. More info on this can be found here:
How can I improve performance of Field.set (perhap using MethodHandles)?
The library can be found here: https://github.com/LanternPowered/Lmbda. For now you will have to use Jitpack to access it (small library so it won't take long to compile):
https://jitpack.io/#LanternPowered/Lmbda
An example for setting a field on a object:
import org.lanternpowered.lmbda.LmbdaFactory;
import org.lanternpowered.lmbda.LmbdaType;
import org.lanternpowered.lmbda.MethodHandlesX;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.function.ObjIntConsumer;
public class LambdaSetterTest {
public static void main(String... args) throws Exception {
final MethodHandles.Lookup lookup = MethodHandlesX.privateLookupIn(TestObject.class, MethodHandles.lookup());
final MethodHandle methodHandle = lookup.findSetter(TestObject.class, "data", int.class);
final ObjIntConsumer<TestObject> setter = LmbdaFactory.create(new LmbdaType<ObjIntConsumer<TestObject>>() {}, methodHandle);
final TestObject object = new TestObject();
System.out.println(100 == object.getData());
setter.accept(object, 10000);
System.out.println(10000 == object.getData());
}
public static class TestObject {
private int data = 100;
int getData() {
return this.data;
}
}
}
And getting a field from a object:
import org.lanternpowered.lmbda.LmbdaFactory;
import org.lanternpowered.lmbda.LmbdaType;
import org.lanternpowered.lmbda.MethodHandlesX;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.function.ToIntFunction;
public class LambdaSetterTest {
public static void main(String... args) throws Exception {
final MethodHandles.Lookup lookup = MethodHandlesX.privateLookupIn(TestObject.class, MethodHandles.lookup());
final MethodHandle methodHandle = lookup.findGetter(TestObject.class, "data", int.class);
final ToIntFunction<TestObject> getter = LmbdaFactory.create(new LmbdaType<ToIntFunction<TestObject>>() {}, methodHandle);
final TestObject object = new TestObject();
System.out.println(100 == getter.applyAsInt(object));
object.setData(10000);
System.out.println(10000 == getter.applyAsInt(object));
}
public static class TestObject {
private int data = 100;
void setData(int value) {
this.data = value;
}
}
}
Consider the following snippet:
public class ReflectionTest {
public static void main(String[] args) {
ReflectionTest test = new ReflectionTest();
String object = new String("Hello!");
// 1. String is accepted as an Object
test.print(object);
// 2. The appropriate method is not found with String.class
try {
java.lang.reflect.Method print
= test.getClass().getMethod("print", object.getClass());
print.invoke(test, object);
} catch (Exception ex) {
ex.printStackTrace(); // NoSuchMethodException!
}
}
public void print(Object object) {
System.out.println(object.toString());
}
}
getMethod() is obviously unaware that a String could be fed to a method that expects an Object (indeed, it's documentation says that it looks for method with the specified name and exactly the same formal parameter types).
Is there a straightforward way to find methods reflectively, like getMethod() does, but taking polymorphism into account, so that the above reflection example could find the print(Object) method when queried with ("print", String.class) parameters?
The reflection tutorial
suggest the use of Class.isAssignableFrom() sample for finding print(String)
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods) {
String mname = m.getName();
if (!mname.startsWith("print") {
continue;
}
Type[] pType = m.getGenericParameterTypes();
if ((pType.length != 1)
|| !String.class.isAssignableFrom(pType[0].getClass())) {
continue;
}
}
The easy way to do this is via java.beans.Statement or java.beans.Expression. Does all these hard yards for you.
getMethod() is obviously unaware that
a String could be fed to a method
that expects an Object
'Unaware' is a strange way to put it. getMethod() adheres to its specification. You have to supply the formal parameters, not the types of the actual arguments.
FYI, it is how I invoke method using reflection with multiple parameters without giving their types.
public class MyMethodUtils {
/**
* Need to pass parameter classes
*/
public static Object invoke(Object invoker, String methodName, Object[] parameters, Class[] parameterClasses) throws Exception {
Method method = invoker.getClass().getMethod(methodName, parameterClasses);
Object returnValue = method.invoke(invoker, parameters);
return returnValue;
}
/**
* No need to pass parameter classes
*/
public static Object invoke(Object invoker, String methodName, Object[] parameters) throws Exception {
Method[] allMethods = invoker.getClass().getDeclaredMethods();
Object returnValue = null;
boolean isFound = false;
for (Method m : allMethods) {
String mname = m.getName();
if (!mname.equals(methodName)) {
continue;
}
Class[] methodParaClasses = m.getParameterTypes();
for (int i = 0; i < methodParaClasses.length; i++) {
Class<?> parameterClass = parameters[i].getClass();
Class<?> methodParaClass = methodParaClasses[i];
boolean isAssignable = methodParaClass.isAssignableFrom(parameterClass);
if (!isAssignable) {
continue;
}
}
returnValue = m.invoke(invoker, parameters);
isFound = true;
}
if (!isFound) {
throw new RuntimeException("Cannot find such method");
}
return returnValue;
}
}
Sample Usage:
MyMethodUtils.invoke(student, "setNameAndMarks", new Object[] { "John", marks }, new Class[] { String.class, Collection.class });
MyMethodUtils.invoke(student, "setNameAndMarks", new Object[] { "John", marks });
However, for the method invoke(Object invoker, String methodName, Object[] parameters), it is possible to invoke wrong method if the signature is ambiguous. For example, if there is two methods for the invoker:
public void setNameAndMarks(String name, Collection<Integer> marks);
public void setNameAndMarks(String name, ArrayList<Integer> marks);
Passing the following parameter may invoke wrong method
setNameAndMarks("John", new ArrayList<Integer>());
Consider the following snippet:
public class ReflectionTest {
public static void main(String[] args) {
ReflectionTest test = new ReflectionTest();
String object = new String("Hello!");
// 1. String is accepted as an Object
test.print(object);
// 2. The appropriate method is not found with String.class
try {
java.lang.reflect.Method print
= test.getClass().getMethod("print", object.getClass());
print.invoke(test, object);
} catch (Exception ex) {
ex.printStackTrace(); // NoSuchMethodException!
}
}
public void print(Object object) {
System.out.println(object.toString());
}
}
getMethod() is obviously unaware that a String could be fed to a method that expects an Object (indeed, it's documentation says that it looks for method with the specified name and exactly the same formal parameter types).
Is there a straightforward way to find methods reflectively, like getMethod() does, but taking polymorphism into account, so that the above reflection example could find the print(Object) method when queried with ("print", String.class) parameters?
The reflection tutorial
suggest the use of Class.isAssignableFrom() sample for finding print(String)
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods) {
String mname = m.getName();
if (!mname.startsWith("print") {
continue;
}
Type[] pType = m.getGenericParameterTypes();
if ((pType.length != 1)
|| !String.class.isAssignableFrom(pType[0].getClass())) {
continue;
}
}
The easy way to do this is via java.beans.Statement or java.beans.Expression. Does all these hard yards for you.
getMethod() is obviously unaware that
a String could be fed to a method
that expects an Object
'Unaware' is a strange way to put it. getMethod() adheres to its specification. You have to supply the formal parameters, not the types of the actual arguments.
FYI, it is how I invoke method using reflection with multiple parameters without giving their types.
public class MyMethodUtils {
/**
* Need to pass parameter classes
*/
public static Object invoke(Object invoker, String methodName, Object[] parameters, Class[] parameterClasses) throws Exception {
Method method = invoker.getClass().getMethod(methodName, parameterClasses);
Object returnValue = method.invoke(invoker, parameters);
return returnValue;
}
/**
* No need to pass parameter classes
*/
public static Object invoke(Object invoker, String methodName, Object[] parameters) throws Exception {
Method[] allMethods = invoker.getClass().getDeclaredMethods();
Object returnValue = null;
boolean isFound = false;
for (Method m : allMethods) {
String mname = m.getName();
if (!mname.equals(methodName)) {
continue;
}
Class[] methodParaClasses = m.getParameterTypes();
for (int i = 0; i < methodParaClasses.length; i++) {
Class<?> parameterClass = parameters[i].getClass();
Class<?> methodParaClass = methodParaClasses[i];
boolean isAssignable = methodParaClass.isAssignableFrom(parameterClass);
if (!isAssignable) {
continue;
}
}
returnValue = m.invoke(invoker, parameters);
isFound = true;
}
if (!isFound) {
throw new RuntimeException("Cannot find such method");
}
return returnValue;
}
}
Sample Usage:
MyMethodUtils.invoke(student, "setNameAndMarks", new Object[] { "John", marks }, new Class[] { String.class, Collection.class });
MyMethodUtils.invoke(student, "setNameAndMarks", new Object[] { "John", marks });
However, for the method invoke(Object invoker, String methodName, Object[] parameters), it is possible to invoke wrong method if the signature is ambiguous. For example, if there is two methods for the invoker:
public void setNameAndMarks(String name, Collection<Integer> marks);
public void setNameAndMarks(String name, ArrayList<Integer> marks);
Passing the following parameter may invoke wrong method
setNameAndMarks("John", new ArrayList<Integer>());