Related
I'm learning java design patterns and I wonder if I can apply some with following problem. I have class Solider and some child classes, for example: General and Sergeant. I'm creating Solider object and in runtime I want to change this object to General or Sergeant object, or create new Sergeant or General object using created earlier Solider object:
Solider s = new Solider(...);
.....
if (generalCondition) {
General g = createGeneralFromSolider(s);
//or better:
//General g = promoteSoliderToGeneral(s);
} else if (sergeantCondition) {
Sergeant sr = createSergeantFromSolider(s);
//or better:
//Sergeant sr = promoteSoliderToSergeant(s);
}
Firstly I decided to create additional constructor in General/Sergeant Class:
Class General extends Solider {
General(Solider s, Map<String, String> generalSpecificParams) {
//first we are going to copy all solider params to general params (bad idea if we have a lot of params)
this.setParamX(s.getParamX());
....
//then we can assign the rest of general-specific params
this.setGeneralSpecificParams(generalSpecificParams);
}
}
and use it in methods createGeneralFromSolider but I'm not sure if it is elegant way. Main disadvantage is that I create new object, so after calling createGeneralFromSolider I have 2 object in memory. I would rather have one object in memory: General/Sergeant promoted from Solider (object General/Sergeant which earlier was the Solider object). I wonder if I can use some design patter to resolve it. I remember that in C++ there has been something like copying constructors which copying all params from one object to another by assigning all params, one after another. In Java I didn't hear about anything similar.
You would probably need to use a Factory pattern for this kind of situation.
For example:
public class SoldierFactory {
//use getSoldier method to get object of type Soldier
public Soldier getSoldier(String soldierType){
if(soldierType == null){
return null;
}
if(soldierType.equals("case1")){
return new General();
} else if(soldierType.equals("case2")){
return new Sergeant();
} else if(.....
}
return null;
}
}
public class FactoryPatternDemo {
public static void main(String[] args) {
SoldierFactory soldierFactory = new SoldierFactory();
Soldier s1 = soldierFactory.getsoldier("case1");
}
}
I think its better to not create the Soldier ahead of calling Soldier factory. You're going to change it regardless during run-time right?
First of all, when constructing child classes, use super as the first statement of the constructor like so:
class Soldier {
private String rank; // e.g. Pvt, PFC, etc.
private int yearsOfService;
// ... (Standard constructor)
public Soldier(Soldier s) {
this.rank = s.rank; this.yearsOfService = s.yearsOfService;
}
// ... (Getters and Setters)
}
class Sergeant extends Soldier {
private int subordinates;
public Sergeant(Soldier s) {
super(s)
this.rank = "Sergeant"; // overwrites this Sergeant's rank
this.subordinates = 0;
}
}
You could easily encapsulate this in a promoteSoldierToSergeant method. However, this can lead to telescoping constructors if classes with many attributes are designed naively, or necessitate your map-based workaround. To resolve this, I'm personally a big fan of the Builder pattern, but you can also consider the Factory pattern.
Your question regarding "copying constructors" is perhaps best addressed by reading up on the Clonable interface, but be aware of the differences between shallow and deep copies, and the implications for your classes and data structures.
I think your approach is totally acceptable. If you have an object X, that you want to turn into Y, you can do it in Y constructor, copying all necessary fields.
You could as well use a builder, or static factory methods, but either way you'll have to copy fields, as there's no automatic copy constructor in java (except if you use some dedicated library such as lombok, which can provide full-args constructors from annotations)
You worry about having 2 objects in memory. But if you remove every reference of the original Soldier, the garbage collector will destroy it.
Last thing, as mentionned by #tsolakp , is it a good design to have General inheriting from Soldier? Couldn't it be just a "grade" variable, or something like that, to reflect this state? It's a common mistake to overuse inheritance where composition would be sufficient, and would cause less troubles.
What you want could be achieved using Reflections.
That way you can automatically copy fields from the instance of parent to child class.
Your code would look something like this:
public static void copyObject(Object src, Object dest)
throws IllegalArgumentException, IllegalAccessException,
NoSuchFieldException, SecurityException {
for (Field field : src.getClass().getFields()) {
dest.getClass().getField(field.getName()).set(dest, field.get(src));
}
}
public static General createGeneral (Solider solider, String devision) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
General general = new General();
copyObject(solider, general);
general.setDevision(devision);
return general;
}
The Field import is java.lang.reflect.Field;
========================================================================
Another way would be to use the Apache Bean Utils.
Than, you can use it's cloneBean(Object Bean) method like this:
General general = cloneBean(solider);
to copy the fields from solider to general and after that all the fields that are specific to child class (General).
========================================================================
EDIT: It would also be wise to introduce another child class that would be used for "ordinary" soliders if you intended to use the parent class Solider for "ordinary" soliders (which I suppose you do according to your commented method name promoteSoliderToGeneral(Solider s).
So, for example you would have a parent class called MilitaryMan and 3 child classes that extend it: Solider, General and Sergeant.
This way, you can uniformly handle all of the MilitaryMan. And, you can check if the MilitaryMan is a Solider, General or Sergeant with:
if (militaryMan instanceOf Solider) {
// do solider specific processing
...
} else if (militaryMan instanceof General) {
...
} else if (militaryMan instanceof Sergeant) {
...
}
I think it would be cleaner this way.
Since Java 8 interfaces could have default methods.
I know how to invoke the method explicitly from the implementing method, i.e.
(see Explicitly calling a default method in Java)
But how do I explicitly invoke the default method using reflection for example on a proxy?
Example:
interface ExampleMixin {
String getText();
default void printInfo(){
System.out.println(getText());
}
}
class Example {
public static void main(String... args) throws Exception {
Object target = new Object();
Map<String, BiFunction<Object, Object[], Object>> behavior = new HashMap<>();
ExampleMixin dynamic =
(ExampleMixin) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[]{ExampleMixin.class}, (Object proxy, Method method, Object[] arguments) -> {
//custom mixin behavior
if(behavior.containsKey(method.getName())) {
return behavior.get(method.getName()).apply(target, arguments);
//default mixin behavior
} else if (method.isDefault()) {
//this block throws java.lang.IllegalAccessException: no private access for invokespecial
return MethodHandles.lookup()
.in(method.getDeclaringClass())
.unreflectSpecial(method, method.getDeclaringClass())
.bindTo(target)
.invokeWithArguments();
//no mixin behavior
} else if (ExampleMixin.class == method.getDeclaringClass()) {
throw new UnsupportedOperationException(method.getName() + " is not supported");
//base class behavior
} else{
return method.invoke(target, arguments);
}
});
//define behavior for abstract method getText()
behavior.put("getText", (o, a) -> o.toString() + " myText");
System.out.println(dynamic.getClass());
System.out.println(dynamic.toString());
System.out.println(dynamic.getText());
//print info should by default implementation
dynamic.printInfo();
}
}
Edit: I know a similar question has been asked in How do I invoke Java 8 default methods refletively, but this has not solved my problem for two reasons:
the problem described in that question aimed on how to invoked it via reflection in general - so no distinction between default and overriden method was made - and this is simple, you only need an instance.
one of the answers - using method handles - does only work with nasty hack (imho) like changing access modifiers to fields of the lookup class, which is the same category of "solutions" like this: Change private static final field using Java reflection: it's good to know it's possible, but I wouldn't use it in production - I'm looking for an "official" way to do it.
The IllegalAccessException is thrown in unreflectSpecial
Caused by: java.lang.IllegalAccessException: no private access for invokespecial: interface example.ExampleMixin, from example.ExampleMixin/package
at java.lang.invoke.MemberName.makeAccessException(MemberName.java:852)
at java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:1568)
at java.lang.invoke.MethodHandles$Lookup.unreflectSpecial(MethodHandles.java:1227)
at example.Example.lambda$main$0(Example.java:30)
at example.Example$$Lambda$1/1342443276.invoke(Unknown Source)
I've been troubled by similar issues as well when using MethodHandle.Lookup in JDK 8 - 10, which behave differently. I've blogged about the correct solution here in detail.
This approach works in Java 8
In Java 8, the ideal approach uses a hack that accesses a package-private constructor from Lookup:
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Constructor;
import java.lang.reflect.Proxy;
interface Duck {
default void quack() {
System.out.println("Quack");
}
}
public class ProxyDemo {
public static void main(String[] a) {
Duck duck = (Duck) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[] { Duck.class },
(proxy, method, args) -> {
Constructor<Lookup> constructor = Lookup.class
.getDeclaredConstructor(Class.class);
constructor.setAccessible(true);
constructor.newInstance(Duck.class)
.in(Duck.class)
.unreflectSpecial(method, Duck.class)
.bindTo(proxy)
.invokeWithArguments(args);
return null;
}
);
duck.quack();
}
}
This is the only approach that works with both private-accessible and private-inaccessible interfaces. However, the above approach does illegal reflective access to JDK internals, which will no longer work in a future JDK version, or if --illegal-access=deny is specified on the JVM.
This approach works on Java 9 and 10, but not 8
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Proxy;
interface Duck {
default void quack() {
System.out.println("Quack");
}
}
public class ProxyDemo {
public static void main(String[] a) {
Duck duck = (Duck) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[] { Duck.class },
(proxy, method, args) -> {
MethodHandles.lookup()
.findSpecial(
Duck.class,
"quack",
MethodType.methodType(void.class, new Class[0]),
Duck.class)
.bindTo(proxy)
.invokeWithArguments(args);
return null;
}
);
duck.quack();
}
}
Solution
Simply implement both of the above solutions and check if your code is running on JDK 8 or on a later JDK and you'll be fine. Until you're not :)
If you use a concrete impl class as lookupClass and caller for the invokeSpecial it should correctly invoke the default implementation of the interface (no hack for private access needed):
Example target = new Example();
...
Class targetClass = target.getClass();
return MethodHandles.lookup()
.in(targetClass)
.unreflectSpecial(method, targetClass)
.bindTo(target)
.invokeWithArguments();
This of course only works if you have a reference to a concrete object implementing the interface.
Edit: this solution will only work if the class in question (Example in the code above), is private accessible from the caller code, e.g. an anonymous inner class.
The current implementation of the MethodHandles/Lookup class will not allow to call invokeSpecial on any class that is not private accessible from the current caller class. There are various work-arounds available, but all of them require the use of reflection to make constructors/methods accessible, which will probably fail in case a SecurityManager is installed.
In Java 16 (from the documentation, which also has more complex examples):
Object proxy = Proxy.newProxyInstance(loader, new Class[] { A.class },
(o, m, params) -> {
if (m.isDefault()) {
// if it's a default method, invoke it
return InvocationHandler.invokeDefault(o, m, params);
}
});
}
If all you have is an interface, and all you have access to is a class object is an interface that extends your base interface, and you want to call the default method without a real instance of a class that implements the interface, you can:
Object target = Proxy.newProxyInstance(classLoader,
new Class[]{exampleInterface}, (Object p, Method m, Object[] a) -> null);
Create an instance of the interface, and then construct the MethodHandles.Lookup using reflection:
Constructor<MethodHandles.Lookup> lookupConstructor =
MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE);
if (!lookupConstructor.isAccessible()) {
lookupConstructor.setAccessible(true);
}
And then use that lookupConstructor to create a new instance of your interface that will allow private access to invokespecial. Then invoke the method on the fake proxy target you made earlier.
lookupConstructor.newInstance(exampleInterface,
MethodHandles.Lookup.PRIVATE)
.unreflectSpecial(method, declaringClass)
.bindTo(target)
.invokeWithArguments(args);
Use:
Object result = MethodHandles.lookup()
.in(method.getDeclaringClass())
.unreflectSpecial(method, method.getDeclaringClass())
.bindTo(target)
.invokeWithArguments();
We can see how spring process default method.
try invoke public method MethodHandles.privateLookupIn(Class,Lookup) first. This should success on jdk9+.
try create a Lookup with package private constructor MethodHandles.Lookup(Class).
fallback to MethodHandles.lookup().findSpecial(...)
https://github.com/spring-projects/spring-data-commons/blob/2.1.8.RELEASE/src/main/java/org/springframework/data/projection/DefaultMethodInvokingMethodInterceptor.java
T. Neidhart answer almost worked but I got the java.lang.IllegalAccessException: no private access for invokespecial
Changing to use MethodHandles.privateLookup() solved it
return MethodHandles.privateLookupIn(clazz,MethodHandles.lookup())
.in(clazz)
.unreflectSpecial(method, clazz)
.bindTo(proxy)
.invokeWithArguments(args);
Here's a full example, the idea is that a user that extends a provided IMap can access nested nested map's with he's custom interface
interface IMap {
Object get(String key);
default <T> T getAsAny(String key){
return (T)get(key);
}
default <T extends IMap> T getNestedAs(String key, Class<T> clazz) {
Map<String,Object> nested = getAsAny(key);
return (T)Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz}, (proxy, method, args) -> {
if (method.getName().equals("get")){
return nested.get(args[0]);
}
return MethodHandles.privateLookupIn(clazz, MethodHandles.lookup())
.in(clazz)
.unreflectSpecial(method, clazz)
.bindTo(proxy)
.invokeWithArguments(args);
}
);
}
}
interface IMyMap extends IMap{
default Integer getAsInt(String key){
return getAsAny(key);
}
default IMyMap getNested(String key){
return getNestedAs(key,IMyMap.class);
}
}
#Test
public void test(){
var data =Map.of("strKey","strValue", "nstKey", Map.of("intKey",42));
IMyMap base = data::get;
IMyMap myMap = base.getNested("nstKey");
System.out.println( myMap.getAsInt("intKey"));
}
Lukas' answer works on Android 8+ (earlier releases do not have default methods) but relies on a private API that was blocked in later Android releases. Fortunately, the alternative constructor also works and is in grey list (unsupported) for now. The example (written in Kotlin) can be seen here.
#get:RequiresApi(26)
private val newLookup by lazy #TargetApi(26) {
MethodHandles.Lookup::class.java.getDeclaredConstructor(Class::class.java, Int::class.java).apply {
isAccessible = true
}
}
#RequiresApi(26)
fun InvocationHandler.invokeDefault(proxy: Any, method: Method, vararg args: Any?) =
newLookup.newInstance(method.declaringClass, 0xf) // ALL_MODES
.unreflectSpecial(method, method.declaringClass)
.bindTo(proxy)
.invokeWithArguments(*args)
I'm trying to port some C# code to Java and I'm having trouble with a proxy implementation.
I have this class:
public class Comic
{
public int ComicID;
private List<Volume> _volumes;
public List<Volume> GetVolumes()
{
return _volumes;
}
public void SetVolumes(List<Volume> volumes)
{
_volumes = volumes;
}
}
And I'm trying to add an interceptor to a specific Method call for this entity, but I also need access to it's fields since I'll be using them.
After looking around for how to implement proxies in Java, I got this:
public void Load(Class<?> type)
{
// - type is a Comic.class
ClassLoader appLoader = this.getClass().getClassLoader();
MyProxyHandler proxyHandler = new MyProxyHandler();
Object proxy = Proxy.newProxyInstance(appLoader, new Class[] { ComicInterface.class }, proxyHandler);
}
The problem is, the type is dynamic so I don't know which will be, and I don't want the code to have a requirement of having interfaces of everything, so I looked up on how to build a dynamic interface:
public class InterfaceLoader extends ClassLoader
{
public InterfaceLoader(ClassLoader loader)
{
super(loader);
}
public Class<?> GetInterface(Class<?> type) throws Exception
{
String interfaceName = type.getName() + "$Proxy";
Class<?> interfaceType = null;
interfaceType = findLoadedClass(interfaceName);
if (interfaceType != null) { return interfaceType; }
// - According to the link
byte[] classData = new InterfaceBuilder().BuildInterface(interfaceName, type.getDeclaredMethods());
return defineClass(interfaceName, classBytes, 0, classBytes.length);
}
}
And then I'd use it with the Proxy:
public void Load(Class<?> type)
{
// - type is a Comic.class
ClassLoader appLoader = this.getClass().getClassLoader();
InterfaceLoader loader = new InterfaceLoader(appLoader);
Class<?> dynamicInterface = loader.GetInterface(type);
// - dynamicInterface on debug is "console.Comic$Proxy", seems fine
MyProxyHandler proxyHandler = new MyProxyHandler();
Object proxy = Proxy.newProxyInstance(appLoader, new Class[] { dynamicInterface }, proxyHandler);
}
The exception I get is
java.lang.IllegalArgumentException: interface console.Comic$Proxy is not visible from class loader
I've looked up the exception but only found two solutions, which is to make sure the name doesn't conflict with any type (and I'm sure it doesn't) and to use this.getClass().getClassLoader() instead of type.getClass().getClassLoader() (same error).
What am I doing wrong?
And another issue is, how can I get the "original" object so I can get/set values from fields like ComicID? Using the above proxy method I can intercept methods fine, but I lose access to it's fields.
I've read about it here to get the InvocationHandler, but I don't know how to get the object from the handler and I couldn't find an example with it.
There is only one classLoader that can see your dynamically generated interface, and that is the InterfaceLoader loader. You can probably make the error go away by passing that to newProxyInstance.
But, I'm pretty sure this won't do you any good. None of the java code you are writing is loaded by that class loader, so you won't be able to call through that interface except by reflection.
If you want to use a proxy, then you'll have to make Comic implement an interface, and then use that interface everywhere. In that case, the answer to your next question is that you would pass an instance of the original object to your proxy handler constructor.
If you really want to do this dynamic interception thing, you probably want to look at using ASM (http://asm.ow2.org/) or Javaassist to build a dynamic subclass of Comic that overrides the method you want to intercept, instead of using a proxy.
How would I modify this so the resulting Collection (newNodes) is the same type as the incoming one (nodes)?
public void setNodes(Collection<NodeInfo> nodes) {
Collection<NodeInfo> newNodes = new TreeSet<NodeInfo>();
for (NodeInfo ni: nodes) {
newNodes.add(ni.clone());
}
}
I suspect it's something like...
public void setNodes(<T extends Collection<NodeInfo>> nodes) {
Collection<NodeInfo> newNodes = new T<NodeInfo>()
Is this possible?
Unfortunately, it's not possible as you've written it in Java. If you need this effect, you've got a few choices:
If you're trying to optimize for a particular kind of collection, you can use an instanceof check to detect it. (For instance the Guava libraries often do this to detect immutable collections and handle them specially.)
If you really just need one collection to populate, you can ask the caller to provide you one.
public <C extends Collection<NodeInfo>> void setNodes(C nodes, C newNodes) {
for (NodeInfo ni : nodes) {
newNodes.add(ni);
}
}
If you need the ability to make an arbitrary number of these collections on demand, then you can define a factory interface and make the caller provide an instance of it:
interface Factory<C extends Collection<NodeInfo>> {
C newCollection();
}
public <C extends Collection<NodeInfo>> void setNodes(C nodes, Factory<C> factory) {
C newNodes = factory.newCollection();
for (NodeInfo ni : nodes) {
newNodes.add(ni);
}
}
Close, but no cigar. If I understand what you want to do, your method should look like:
public <T extends NodeInfo> void setNodes(Collection<T> nodes) {
Collection<T> newNodes = new TreeSet<T>();
for(T t : nodes) {
newNodes.add(t);
}
}
Unfortunately, you cannot do new T in Java: Since generics are implemented in Java via type erasure, the type information that is provided by a type parameter is only statically usable information, i.e. no longer available at runtime. Hence Java does not permit generic creation of objects (cf. Angelika Lange's Generics FAQ).
Alternatively, you could use:
a type token, i.e. use a Class<T> object as parameter to make the type available at runtime
use the signature void setNodes(Collection<NodeInfo> nodes, Collection<NodeInfo> newNodes) if you are able to create the suitable Collection elsewhere
use a standard Collection implementation if one is suitable, e.g. ArrayList<NodeInfo>
deep clone nodes, e.g. using The Cloning Library:
Cloner cloner=new Cloner();
#SuppressWarnings("unchecked") Collection<NodeInfo> newNodes = cloner.deepClone(nodes);
Note that many of the Collection implementations in the JDK implement Cloneable themselves. One "best effort" approach might be like this:
public Collection<NodeInfo> setNodes(Collection<NodeInfo> nodes) throws CloneNotSupportedException {
Collection<NodeInfo) newNodes;
if (nodes instanceof Cloneable)
newNodes = (Collection<NodeInfo>) newNodes.clone();
else
// Fallback in case we have a non-cloneable collection
newNodes = new TreeSet<NodeInfo>();
newNodes.clear();
for (NodeInfo ni: nodes) {
newNodes.add(ni.clone());
}
return newNodes;
}
This returns the same kind of collection for many inputs, but will fall back to returning a TreeSet as a default if it can't do any better.
Is any practical way to reference a method on a class in a type-safe manner? A basic example is if I wanted to create something like the following utility function:
public Result validateField(Object data, String fieldName,
ValidationOptions options) { ... }
In order to call it, I would have to do:
validateField(data, "phoneNumber", options);
Which forces me to either use a magic string, or declare a constant somewhere with that string.
I'm pretty sure there's no way to get around that with the stock Java language, but is there some kind of (production grade) pre-compiler or alternative compiler that may offer a work around? (similar to how AspectJ extends the Java language) It would be nice to do something like the following instead:
public Result validateField(Object data, Method method,
ValidationOptions options) { ... }
And call it with:
validateField(data, Person.phoneNumber.getter, options);
As others mention, there is no real way to do this... and I've not seen a precompiler that supports it. The syntax would be interesting, to say the least. Even in your example, it could only cover a small subset of the potential reflective possibilities that a user might want to do since it won't handle non-standard accessors or methods that take arguments, etc..
Even if it's impossible to check at compile time, if you want bad code to fail as soon as possible then one approach is to resolve referenced Method objects at class initialization time.
Imagine you have a utility method for looking up Method objects that maybe throws error or runtime exception:
public static Method lookupMethod( Class c, String name, Class... args ) {
// do the lookup or throw an unchecked exception of some kind with a really
// good error message
}
Then in your classes, have constants to preresolve the methods you will use:
public class MyClass {
private static final Method GET_PHONE_NUM = MyUtils.lookupMethod( PhoneNumber.class, "getPhoneNumber" );
....
public void someMethod() {
validateField(data, GET_PHONE_NUM, options);
}
}
At least then it will fail as soon as MyClass is loaded the first time.
I use reflection a lot, especially bean property reflection and I've just gotten used to late exceptions at runtime. But that style of bean code tends to error late for all kinds of other reasons, being very dynamic and all. For something in between, the above would help.
There isn't anything in the language yet - but part of the closures proposal for Java 7 includes method literals, I believe.
I don't have any suggestions beyond that, I'm afraid.
Check out https://proxetta.jodd.org/refs/methref. It uses the Jodd proxy library (Proxetta) to proxy your type. Not sure about its performance characteristics, but it does provide type safety.
An example: Suppose Str.class has method .boo(), and you want to get its name as the string "boo":
String methodName = Methref.of(Str.class).name(Str::boo);
There's more to the API than the example above: https://oblac.github.io/jodd-site/javadoc/jodd/methref/Methref.html
Is any practical way to reference a method on a class in a type-safe manner?
First of all, reflection is type-safe. It is just that it is dynamically typed, not statically typed.
So, assuming that you want a statically typed equivalent of reflection, the theoretical answer is that it is impossible. Consider this:
Method m;
if (arbitraryFunction(obj)) {
m = obj.getClass().getDeclaredMethod("foo", ...);
} else {
m = obj.getClass().getDeclaredMethod("bar", ...);
}
Can we do this so that that runtime type exceptions cannot happen? In general NO, since this would entail proving that arbitraryFunction(obj) terminates. (This is equivalent to the Halting Problem, which is proven to be unsolvable in general, and is intractable using state-of-the-art theorem proving technology ... AFAIK.)
And I think that this road-block would apply to any approach where you could inject arbitrary Java code into the logic that is used to reflectively select a method from an object's class.
To my mind, the only moderately practical approach at the moment would be to replace the reflective code with something that generates and compiles Java source code. If this process occurs before you "run" the application, you've satisfied the requirement for static type-safety.
I was more asking about reflection in which the result is always the same. I.E. Person.class.getMethod("getPhoneNumber", null) would always return the same method and it's entirely possible to resolve it at compile time.
What happens if after compiling the class containing this code, you change Person to remove the getPhoneNumber method?
The only way you can be sure that you can resolve getPhoneNumber reflectively is if you can somehow prevent Person from being changed. But you can't do that in Java. Runtime binding of classes is a fundamental part of the language.
(For record, if you did that for a method that you called non-reflectively, you would get an IncompatibleClassChangeError of some kind when the two classes were loaded ...)
It has been pointed out that in Java 8 and later you could declare your validator something like this:
public Result validateField(Object data,
SomeFunctionalInterface function,
ValidationOptions options) { ... }
where SomeFunctionalInterface corresponds to the (loosely speaking) common signature of the methods you are validating.
Then you can call it with a method reference; e.g.
validateField(data, SomeClass::someMethod, options)
This is approach is statically type-safe. You will get a compilation error if SomeClass doesn't have someMethod or if it doesn't conform to SomeFunctionalInterface.
But you can't use a string to denote the method name. Looking up a method by name would entail either reflection ... or something else that side-steps static (i.e. compile time / load time) type safety.
Java misses the syntax sugar to do something as nice as Person.phoneNumber.getter. But if Person is an interface, you could record the getter method using a dynamic proxy. You could record methods on non-final classes as well using CGLib, the same way Mockito does it.
MethodSelector<Person> selector = new MethodSelector<Person>(Person.class);
selector.select().getPhoneNumber();
validateField(data, selector.getMethod(), options);
Code for MethodSelector: https://gist.github.com/stijnvanbael/5965609
Inspired by mocking frameworks, we could dream up the following syntax:
validator.validateField(data, options).getPhoneNumber();
Result validationResult = validator.getResult();
The trick is the generic declaration:
class Validator {
public <T> T validateField(T data, options) {...}
}
Now the return type of the method is the same as your data object's type and you can use code completion (and static checking) to access all the methods, including the getter methods.
As a downside, the code isn't quite intuitive to read, since the call to the getter doesn't actually get anything, but instead instructs the validator to validate the field.
Another possible option would be to annotate the fields in your data class:
class FooData {
#Validate(new ValidationOptions(...))
private PhoneNumber phoneNumber;
}
And then just call:
FooData data;
validator.validate(data);
to validate all fields according to the annotated options.
The framework picklock lets you do the following:
class Data {
private PhoneNumber phoneNumber;
}
interface OpenData {
PhoneNumber getPhoneNumber(); //is mapped to the field phoneNumber
}
Object data = new Data();
PhoneNumber number = ObjectAccess
.unlock(data)
.features(OpenData.class)
.getPhoneNumber();
This works in a similar way setters and private methods. Of course, this is only a wrapper for reflection, but the exception does not occur at unlocking time not at call time. If you need it at build time, you could write a unit test with:
assertThat(Data.class, providesFeaturesOf(OpenData.class));
I found a way to get the Method instance using Lambdas. It works only on interface methods though currently.
It works using net.jodah:typetools which is a very lightweight library.
https://github.com/jhalterman/typetools
public final class MethodResolver {
private interface Invocable<I> {
void invokeWithParams(I instance, Class<?>[] parameterTypes) throws Throwable;
}
interface ZeroParameters<I, R> extends Invocable<I> {
R invoke(I instance) throws Throwable;
#Override
default void invokeWithParams(I instance, Class<?>[] parameterTypes) throws Throwable {
invoke(instance);
}
}
public static <I, R> Method toMethod0(ZeroParameters<I, R> call) {
return toMethod(ZeroParameters.class, call, 1);
}
interface OneParameters<I, P1, R> extends Invocable<I> {
R invoke(I instance, P1 p1) throws Throwable;
#Override
default void invokeWithParams(I instance, Class<?>[] parameterTypes) throws Throwable {
invoke(instance, param(parameterTypes[1]));
}
}
public static <I, P1, R> Method toMethod1(OneParameters<I, P1, R> call) {
return toMethod(OneParameters.class, call, 2);
}
interface TwoParameters<I, P1, P2, R> extends Invocable<I> {
R invoke(I instance, P1 p1, P2 p2) throws Throwable;
#Override
default void invokeWithParams(I instance, Class<?>[] parameterTypes) throws Throwable {
invoke(instance, param(parameterTypes[1]), param(parameterTypes[2]));
}
}
public static <I, P1, P2, R> Method toMethod2(TwoParameters<I, P1, P2, R> call) {
return toMethod(TwoParameters.class, call, 3);
}
private static final Map<Class<?>, Object> parameterMap = new HashMap<>();
static {
parameterMap.put(Boolean.class, false);
parameterMap.put(Byte.class, (byte) 0);
parameterMap.put(Short.class, (short) 0);
parameterMap.put(Integer.class, 0);
parameterMap.put(Long.class, (long) 0);
parameterMap.put(Float.class, (float) 0);
parameterMap.put(Double.class, (double) 0);
}
#SuppressWarnings("unchecked")
private static <T> T param(Class<?> type) {
return (T) parameterMap.get(type);
}
private static <I> Method toMethod(Class<?> callType, Invocable<I> call, int responseTypeIndex) {
Class<?>[] typeData = TypeResolver.resolveRawArguments(callType, call.getClass());
Class<?> instanceClass = typeData[0];
Class<?> responseType = responseTypeIndex != -1 ? typeData[responseTypeIndex] : Void.class;
AtomicReference<Method> ref = new AtomicReference<>();
I instance = createProxy(instanceClass, responseType, ref);
try {
call.invokeWithParams(instance, typeData);
} catch (final Throwable e) {
throw new IllegalStateException("Failed to call no-op proxy", e);
}
return ref.get();
}
#SuppressWarnings("unchecked")
private static <I> I createProxy(Class<?> instanceClass, Class<?> responseType,
AtomicReference<Method> ref) {
return (I) Proxy.newProxyInstance(MethodResolver.class.getClassLoader(),
new Class[] {instanceClass},
(proxy, method, args) -> {
ref.set(method);
return parameterMap.get(responseType);
});
}
}
Usage:
Method method = MethodResolver.toMethod2(SomeIFace::foobar);
System.out.println(method); // public abstract example.Result example.SomeIFace.foobar(java.lang.String,boolean)
Method get = MethodResolver.<Supplier, Object>toMethod0(Supplier::get);
System.out.println(get); // public abstract java.lang.Object java.util.function.Supplier.get()
Method accept = MethodResolver.<IntFunction, Integer, Object>toMethod1(IntFunction::apply);
System.out.println(accept); // public abstract java.lang.Object java.util.function.IntFunction.apply(int)
Method apply = MethodResolver.<BiFunction, Object, Object, Object>toMethod2(BiFunction::apply);
System.out.println(apply); // public abstract java.lang.Object java.util.function.BiFunction.apply(java.lang.Object,java.lang.Object)
Unfortunately you have to create a new interface and method based on the parameter count and whether the method returns void or not.
However, if you have a somewhat fixed/limited method signature/parameter types, then this becomes quite handy.