Related
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;
}
}
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.
My company has an application server that receives sets of instructions in their own bespoke XTML syntax. As this is limited, there's a special "drop to Java" command that sends arguments to a JVM (1.6.0_39). Arguments are passed as "in" only, or "in/out", where the special "in/out" variables are a library of mutables for use with this platform.
Previously the only way to receive external configuration was to use a different special command to read from an XTML file. For reasons not worth delving into, this method of configuration is difficult to scale, so I'm working on a way to do this with Java.
The syntax for this configuration was two-tuples of (String,T) where String was the property name in the XTML file, and T was the in/out mutable that the application server would assign the property value to.
I'm attempting to make this transition as seamless as possible, and not have to do annoying string parsing in the application server.
I already have a function
public String[] get(String ... keys)
That retrieves the values from the application servers' keys, but What I really need is a function
public static void get(T ... args)
that accepts the two-tuples. However, note it needs to be static in order to be called from the application server, and my understanding is that T can't be used in a static context.
I'm at a loss for how to approach this problem in a way that doesn't require (at least) two steps, and there is no way to loop over the arguments in the application server.
I know I'm working within a tight set of constraints here, so if the answer is "you have to some messed up stuff", that's fine - I'd just like any insight into another way.
-- edit --
Editing a more specific example.
The configuration is a set of key-value pairs, and can be in a database or a file. The get function is:
public JSONObject get(String ... keys) throws ClassNotFoundException, SQLException, KeyNotFoundException, FileNotFoundException, IOException {
JSONObject response = new JSONObject();
if(this.isDatabase) {
for(int i=0;i<keys.length;i++){
PreparedStatement statement = this.prepare("SELECT value FROM "+this.databaseSchema+"."+this.settingsTableName+" WHERE key = ? LIMIT 1");
statement.setString(1, keys[i]);
ResultSet results = statement.executeQuery();
boolean found = false;
while(results.next()){
String value = results.getString("value");
value = value.replace("\"","");
response.put(keys[i], value);
found = true;
}
if(!found){
throw new KeyNotFoundException(keys[i]);
}
}
} else if (this.isFile) {
boolean[] found = new boolean[keys.length];
BufferedReader br = new BufferedReader(new FileReader(this.settingsFile));
String line;
while((line = br.readLine()) != null ){
String key;
String value;
for(int i=0;i<line.length();i++){
if(line.charAt(i) == '='){
key = line.substring(0,i);
value = line.substring(i+1,line.length());
if(indexOfString(keys,key) != -1){
value = value.replace("\"","");
found[indexOfString(keys,key)] = true;
response.put(key,value);
if(allFound(found)==-1){
return response;
}
}
break;
}
}
}
if(allFound(found)!=-1){
throw new KeyNotFoundException(keys[allFound(found)]);
}
}
return response;
If I had my way, it would look like ...
// ConfigurationReader.java
public class ConfigurationReader{
public ConfigurationReader( ... ){}
public static JSONObject get(String key){
// Get the key
}
}
// ConfigurationInterface.java
public static void get(T ... args){
ConfigurationReader cfgReader = new ConfigurationReader( ... );
for(var i=0;i<args.length;i+=2){
in = args[i];
out = args[i+1];
out = cfgReader.get(in);
}
}
You can use generic types in a static context. Your question is somewhat vague/unclear about how you intend to do this, but consider the example below:
public class Example {
public static void main(String[] args) {
Type t1 = new Type("foo");
Type t2 = new Type("bar");
Type t3 = new Type("baz");
Printer.<Type> printNames(t1, t2, t3);
}
public static class Printer {
#SafeVarargs
public static <T extends Type> void printNames(T... objs) {
for (T obj : objs) {
System.out.println(obj);
}
}
}
public static class Type {
private final String name;
public Type(String name) {
this.name = name;
}
#Override
public final String toString() {
return name;
}
}
}
Printer.<Type> printNames(t1, t2, t3) makes a static reference to the printNames method, parameterized with the Type generic type.
Note that this is type-safe. Attempting to pass an object of a different type into that parameterized method will fail at compile-time (assuming the type is known to be different at that point):
Example.java:8: error: method printNames in class Printer cannot be applied to given types;
Printer.<Type> printNames(t1, t2, t3, "test");
^
required: T[]
found: Type,Type,Type,String
reason: varargs mismatch; String cannot be converted to Type
where T is a type-variable:
T extends Type declared in method <T>printNames(T...)
Edit
Based on your comment, the issue isn't that you're trying use a generic type for your method argument (in the Java-sense of the word generic, anyway); you're simply looking for any non-specific, parent class that both String and your custom type inherit from. There's only one such class: Object.
I'd strongly recommend reconsidering your design if you have any flexibility, since this will make for poor API design. However you can have your method accept an arbitrary number of arbitrarily-typed objects using Object... objs.
For example:
public class Example {
public static void main(String[] args) {
Printer.printNames("a", "b", new Type("foo"), new Type("bar"));
}
public static class Printer {
public static void printNames(Object... objs) {
for (Object obj : objs) {
if (obj instanceof String) {
System.out.println(((String) obj).toUpperCase());
}
else if (obj instanceof Type) {
System.out.println(obj);
}
}
}
}
public static class Type {
private final String name;
public Type(String name) { this.name = name; }
public final String toString() { return name; }
}
}
Based on #nbrooks work, I found a solution. I made a temporary MutableString (to be replaced by the classes provided by the library).
public static class MutableString {
public String value;
public MutableString(){}
}
// One for every mutable type
public static void Pair(String key, MutableString mutable, ApplicationConfiguration appConfig) throws Exception{
mutable.value = appConfig.get(key).toString();
}
public static void Retrieve(Object ... args) throws Exception {
ApplicationConfiguration appConfig = new ApplicationConfiguration( ##args## );
for(int i=0;i<args.length;i+=2){
if(args[i+1].getClass().equals(new MutableString().getClass())){
ApplicationConfiguration.Pair( (String) args[i], (MutableString) args[i+1], appConfig);
} // One for every mutable type
}
}
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;
}
}
}
I have an Enum defined which contains method return type like "String",Float,List,Double etc.
I will be using it in switch case statements.
For example my enum is
public enum MethodType {
DOUBLE,LIST,STRING,ARRAYLIST,FLOAT,LONG;
}
In a property file, I've key value pairs as follows.
Test1=String
Test2=Double
In my code I'm getting the value for the key. I need to use the VALUE in Switch Case to Determine the Type and based on that I've to implement some logic.
For example something like this
switch(MethodType.DOUBLE){
case DOUBLE:
//Dobule logic
}
Can someone please help me to implement this?
I guess this is what you are looking for:
public class C_EnumTest {
public enum MethodType {
DOUBLE,LIST,STRING,ARRAYLIST,FLOAT,LONG;
}
public static void main( String[] args ) {
String value = "DOUBLE";
switch( MethodType.valueOf( value ) ) {
case DOUBLE:
System.out.println( "It's a double" );
break;
case LIST:
System.out.println( "It's a list" );
break;
}
}
}
For not being case sensitive you could do a MethodType.valueOf( value.toUpperCase() ).
This may be a little closer to what you need. You can make the propertyName property anything you need it to be in this case:
public enum MethodType {
STRING("String"),
LONG("Long"),
DOUBLE("Double"),
THING("Thing");
private String propertyName;
MethodType(String propName) {
this.propertyName = propName;
}
public String getPropertyName() {
return propertyName;
}
static MethodType fromPropertyName(String x) throws Exception {
for (MethodType currentType : MethodType.values()) {
if (x.equals(currentType.getPropertyName())) {
return currentType;
}
}
throw new Exception("Unmatched Type: " + x);
}
}
You've defined the enum, but you need to define a variable that is that type. Like this:
public enum MethodType { ... }
public MethodType myMethod;
switch (myMethod)
{
case MethodType.DOUBLE:
//...
break;
case MethodType.LIST:
//...
break;
//...
}
Edit:
Previously, this snippet used var as the variable name, but that's a reserved keyword. Changed to myMethod.
You don't need a switch at all (this is in java btw so might not work for you): Obviously you'll want to add some null checks, better exception handling, etc.
public enum MethodType {
String,LONG,DOUBLE,THING;
static MethodType fromString(String x) throws Exception {
for (MethodType currentType: MethodType.values()){
if (x.equals(currentType.toString())){
return currentType;
}
}
throw new Exception("Unmatched Type");
}
}