In general, I don't want really to change the class of the objects. What I want is to have some mechanism to make client classes to obey to the contracts of the different types. I.e., I don't want a class to be able to call setShareholders on a Person, and it's not important how I can achieve this, I mean that maybe the fact that an entity is a Person can be represented on other ways than using a class Person.
This last paragraph got me thinking that a dynamic proxy may address your need. If you have an "entity" E that "is a Person [that] can be represented on other ways than using a class Person". A proxy could wrap your entity E and "implement"/present only the desired interface Person
(while hiding any other implemented interfaces or implementation detail about E).
Edit: Since the OP found the answer useful, I'm adding a utility class and demo code:
Some code:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Utilities to support using dynamic proxies
*/
public class DynamicProxy {
/**
* An invocation handler that passes calls to its delegate. This class
* could be subclassed to provide dynamic method invocation handling
* while still being able to fall back to the delegate object's methods.
*
* @see InvocationHandler
*/
public static class DelegatingInvocationHandler
implements InvocationHandler {
/** The object this proxy is wrapping */
private final Object delegate;
/**
* Creates a delegate invocation handler around an object
*
* @param object
* The object to wrap
*/
public DelegatingInvocationHandler(final Object delegate) {
this.delegate = delegate;
}
/* (non-Javadoc)
*
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
* java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object invoke(final Object proxy, final Method m,
final Object[] args) throws Throwable {
Object result;
try {
result = m.invoke(delegate, args);
} catch (final InvocationTargetException e) {
throw e.getTargetException();
} catch (final Exception e) {
throw new RuntimeException("unexpected invocation exception: "
+ e.getMessage());
}
return result;
}
}
/**
* Create a dynamic proxy that implements only a specified subset of the
* original object's implemented interfaces. The proxy allows you to
* essentially hide the other interfaces implemented by the original
* object.
*
* @param delegate
* the object that the proxy "proxies" for (a.k.a. the delegate
* that handles the calls the proxy "allows" through)
* @param requiredInterface
* one of the interfaces of the delegate that the proxy exposes
* @param moreInterfaces
* any additional interfaces of the delegate to expose
* @return the proxy
* a proxy for the delegate that can be cast to any of the
* specified interfaces
*/
public static <T> T createSelectiveProxy(final T delegate,
final Class<T> requiredInterface,
final Class<?>... moreInterfaces) {
if (delegate == null) {
throw new IllegalArgumentException(
"The delegate object cannot be null");
}
return createProxy(new DelegatingInvocationHandler(delegate),
requiredInterface, moreInterfaces);
}
/**
* Creates a proxy using the specified invocation handler.
*
* @param object
* the implementing object that proxy wraps
* @param invocationHandler
* the interfaces
* @param moreInterfaces
* the interfaces
* @return the object
*/
@SuppressWarnings("unchecked")
public static <T> T createProxy(final InvocationHandler invocationHandler,
final Class<T> requiredInterface,
final Class<?>... moreInterfaces) {
if (invocationHandler == null) {
throw new IllegalArgumentException(
"The invocation handler cannot be null");
}
final int size = (moreInterfaces != null ? moreInterfaces.length : 0);
final Class<?>[] interfaces = new Class<?>[size + 1];
interfaces[0] = requiredInterface;
System.arraycopy(moreInterfaces, 0, interfaces, 1, moreInterfaces.length);
return (T) Proxy.newProxyInstance(invocationHandler.getClass()
.getClassLoader(), interfaces, invocationHandler);
}
}
Demo:
public class DynamicProxyDemo {
private interface A {
void methodA();
}
private interface B {
void methodB();
}
private static class Foo implements A, B {
public void methodA() {
System.out.println("A");
}
public void methodB() {
System.out.println("B");
}
}
private DynamicProxyDemo() {}
public static void main(final String[] args) {
final Foo foo = new Foo(); // implements both interfaces
// calls foo's methods, but only A methods
final A a = DynamicProxy.createSelectiveProxy(foo, A.class);
// calls foo's methods, but only B methods
final B b = DynamicProxy.createSelectiveProxy(foo, B.class);
// calls foo's methods, but A and B methods
final A ab = DynamicProxy.createSelectiveProxy(foo, A.class, B.class);
System.out.println("\n *** Call a method A.methodA() on proxy 'a'");
a.methodA();
try {
System.out.println("\n *** Call a method B.methodB() on proxy 'a' (throws exception)");
((B) a).methodB();
} catch (final Exception ex) {
ex.printStackTrace(System.out);
}
System.out.println("\n *** Call a method B.methodB() on proxy 'b'");
b.methodB();
try {
System.out.println("\n *** Call a method A.methodA() on proxy 'b' (throws exception)");
((A) b).methodA();
} catch (final Exception ex) {
ex.printStackTrace(System.out);
}
System.out.println("\n *** Call a method A.methodA() on proxy 'ab'");
ab.methodA();
System.out.println("\n *** Call a method B.methodB() on proxy 'ab'");
((B) ab).methodB();
// ClassCastException: $Proxy0 cannot be cast to DynamicProxy$Foo
try {
System.out.println("\n *** Call a method 'A' of class 'Foo' on proxy 'a' (throws exception)");
((Foo) a).methodA();
} catch (final Exception ex) {
ex.printStackTrace(System.out);
}
// ClassCastException: $Proxy1 cannot be cast to DynamicProxy$Foo
try {
System.out.println("\n *** Call a method 'B' of class 'Foo' on proxy 'b' (throws exception)");
((Foo) b).methodB();
} catch (final Exception ex) {
ex.printStackTrace(System.out);
}
// ClassCastException: $Proxy0 cannot be cast to DynamicProxy$B
try {
System.out.println("\n *** Call a method B.methodB() on proxy 'a' (throws exception)");
((B) a).methodB();
} catch (final Exception ex) {
ex.printStackTrace(System.out);
}
// ClassCastException: $DynamicProxy1 cannot be cast to DynamicProxy$A
try {
System.out.println("\n *** Call a method A.methodA() on proxy 'b' (throws exception)");
((A) b).methodA();
} catch (final Exception ex) {
ex.printStackTrace(System.out);
}
}
}
Run:
*** Call a method A.methodA() on proxy 'a'
A
*** Call a method B.methodB() on proxy 'a' (throws exception)
java.lang.ClassCastException: net.bertfernandez.reflection.$Proxy0 cannot be cast to net.bertfernandez.reflection.DynamicProxyDemo$B
at net.bertfernandez.reflection.DynamicProxyDemo.main(DynamicProxyDemo.java:49)
*** Call a method B.methodB() on proxy 'b'
B
*** Call a method A.methodA() on proxy 'b' (throws exception)
java.lang.ClassCastException: net.bertfernandez.reflection.$Proxy1 cannot be cast to net.bertfernandez.reflection.DynamicProxyDemo$A
at net.bertfernandez.reflection.DynamicProxyDemo.main(DynamicProxyDemo.java:59)
*** Call a method A.methodA() on proxy 'ab'
A
*** Call a method B.methodB() on proxy 'ab'
B
*** Call a method 'A' of class 'Foo' on proxy 'a' (throws exception)
java.lang.ClassCastException: net.bertfernandez.reflection.$Proxy0 cannot be cast to net.bertfernandez.reflection.DynamicProxyDemo$Foo
at net.bertfernandez.reflection.DynamicProxyDemo.main(DynamicProxyDemo.java:73)
*** Call a method 'B' of class 'Foo' on proxy 'b' (throws exception)
java.lang.ClassCastException: net.bertfernandez.reflection.$Proxy1 cannot be cast to net.bertfernandez.reflection.DynamicProxyDemo$Foo
at net.bertfernandez.reflection.DynamicProxyDemo.main(DynamicProxyDemo.java:81)
*** Call a method B.methodB() on proxy 'a' (throws exception)
java.lang.ClassCastException: net.bertfernandez.reflection.$Proxy0 cannot be cast to net.bertfernandez.reflection.DynamicProxyDemo$B
at net.bertfernandez.reflection.DynamicProxyDemo.main(DynamicProxyDemo.java:89)
*** Call a method A.methodA() on proxy 'b' (throws exception)
java.lang.ClassCastException: net.bertfernandez.reflection.$Proxy1 cannot be cast to net.bertfernandez.reflection.DynamicProxyDemo$A
at net.bertfernandez.reflection.DynamicProxyDemo.main(DynamicProxyDemo.java:97)