I have implement a dynamic proxy in order to do some operations before my methods started.
now I have a problem when invoking two methods from the proxied class, here is the code:
Dynamic proxy class:
public class IPageProxy implements InvocationHandler {
private Class <? extends IPage> screenClazz;
public IPageProxy(final Class <? extends IPage> screenClazz) {
this.screenClazz = screenClazz;
}
#SuppressWarnings("unchecked")
public static <T extends IPage> T getInstance(final Class<? extends IPage> type)
throws InstantiationException, IllegalAccessException {
List<Class<?>> interfaces = new ArrayList<>();
interfaces.addAll(Arrays.asList(type.getInterfaces()));
return (T) Proxy.newProxyInstance(
type.getClassLoader(),
findInterfaces(type),
new IPageProxy(type)
);
}
static Class<?>[] findInterfaces(final Class<? extends IPage> type) {
Class<?> current = type;
do {
final Class<?>[] interfaces = current.getInterfaces();
if (interfaces.length != 0) {
return interfaces;
}
} while ((current = current.getSuperclass()) != Object.class);
throw new UnsupportedOperationException("The type does not implement any interface");
}
#Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws InvocationTargetException,
IllegalAccessException, IllegalArgumentException, InstantiationException, ParserConfigurationException, XPathExpressionException, NoSuchFieldException, SecurityException {
// before method executed this code will be done
System.out.println("* Dynamic proxy invoke method executed for " + method.getName());
// Invoke original method
return method.invoke(screenClazz.newInstance(), args);
}
}
Main class:
public static void main(String[] args) {
try {
//IEventDesignDialog a = new EventDesignDialog();
IEventDesignDialog a = (IEventDesignDialog)getInstance(EventDesignDialog.class);
a.getEventType().getShow();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
#SuppressWarnings("unchecked")
public static <T extends IPage> T getInstance(final Class<? extends IPage> type) throws InstantiationException, IllegalAccessException {
return (T) IPageProxy.getInstance(type);
}
Proxied class:
public class EventDesignDialog implements IEventDesignDialog{
private String show;
private String dateAndTimeDisplayFormat;
private String eventType;
#Entity(visibileName = "Show")
public IEventDesignDialog getShow() {
System.out.println("get show method invokde successfully");
return this;
}
#Entity(visibileName = "Date And Time display format")
public IEventDesignDialog getDateAndTimeDisplayFormat() {
System.out.println("get date and time display format method invokde successfully");
return this;
}
#Entity(visibileName = "Event Type")
public IEventDesignDialog getEventType() {
System.out.println("get event type method invokde successfully");
return this;
}
}
Actual output:
*** Dynamic proxy invoke method executed for getEventType
get event type method invokde successfully
get show method invokde successfully**
as shown invoke method executed only at the first method invocation after initializing the proxy, second method invoked directly, without proxy functionality is done.
my goal is to execute invoke method each time a method appears at my collection is invoked, the expected result should be as shown below.
Expected output:
*** Dynamic proxy invoke method executed for getEventType
get event type method invokde successfully
* Dynamic proxy invoke method executed for getShow
get show method invokde successfully**
please let me know if more explanations needed.
I have solve this issue by creating an Interface with default method that return proxy instance, then returned it after executing the invoked method functionality:
updated code:
public interface IPage {
default <T extends IPage> T getProxyInstance() {
try {
return (T) IPageProxy.getInstance(this.getClass());
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
My Page Interface:
#Page(path = "MyPath")
public interface IEventDesignDialog extends IPage{
#Entity(visibileName = "Show")
public IEventDesignDialog getShow();
#Entity(visibileName = "Date And Time display format")
public IEventDesignDialog getDateAndTimeDisplayFormat();
#Entity(visibileName = "Event Type")
public IEventDesignDialog getEventType();
}
My Page class:
#Page(path = "MyPath")
public class EventDesignDialog implements IEventDesignDialog{
#Entity(visibileName = "Show")
public IEventDesignDialog getShow() {
System.out.println("get show method invokde successfully");
return getProxyInstance();
}
#Entity(visibileName = "Date And Time display format")
public IEventDesignDialog getDateAndTimeDisplayFormat() {
System.out.println("get date and time display format method invokde successfully");
return getProxyInstance();
}
#Entity(visibileName = "Event Type")
public IEventDesignDialog getEventType() {
System.out.println("get event type method invokde successfully");
return getProxyInstance();
}
}
main class:
public class Main {
public static void main(String[] args) {
try {
IEventDesignDialog a = ((IEventDesignDialog)getInstance(EventDesignDialog.class)).getEventType().getShow();
((IShowDesignDialog)getInstance(ShowDesignDialog.class)).getShowName().getShowType();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
#SuppressWarnings("unchecked")
public static <T extends IPage> T getInstance(final Class<? extends IPage> type) throws InstantiationException, IllegalAccessException {
return (T) IPageProxy.getInstance(type);
}
}
IProxy Page stay the same without changes.
What is happening is that first you proxy only the first invocation , but then you invoke getShow() on a non proxified class, and this is why you get the result like you mentioned. If you want to achieve the goal you mentioned you need to make another proxy based on created instance rather than on just on the class.
Update:
I will provide example code, you can paste in any java file and execute it.
Where you see the TODO, you can place your own logic depending how you want to supply proxy. See NOTE for important moments. I placed all classes in one file for simplicity of demonstration.
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
class Scratch {
public static void main(String[] args) {
try {
IEventDesignDialog a = proxy(EventDesignDialog.class);
a.getEventType().getShow();
a.getDateAndTimeDisplayFormat().getShow();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
#SuppressWarnings("unchecked")
private static <T extends IPage> T proxy(final Class<? extends IPage> type) throws InstantiationException, IllegalAccessException {
return (T) IPageProxy.proxy(type);
}
}
interface IPage{}
interface IEventDesignDialog extends IPage{
IEventDesignDialog getShow();
IEventDesignDialog getEventType();
IEventDesignDialog getDateAndTimeDisplayFormat();
}
class EventDesignDialog implements IEventDesignDialog{
public IEventDesignDialog getShow() {
System.out.println("get show method invoked successfully");
//NOTE: this will be treated as same proxy but not this
return this;
}
public IEventDesignDialog getDateAndTimeDisplayFormat() {
System.out.println("get date and time display format method invoked successfully");
// NOTE: we supply some iinstance which will be proxied
return new MyIEventDesignDialog();
}
public IEventDesignDialog getEventType() {
System.out.println("get event type method invoked successfully");
//NOTE: this will be treated as same proxy but not this
return this;
}
}
class IPageProxy implements InvocationHandler {
private IPage instance;
private List<Class<?>> interfaces;
public IPageProxy(IPage instance, List<Class<?>> interfaces) {
this.instance = instance;
this.interfaces = interfaces;
}
#SuppressWarnings("unchecked")
public static <T extends IPage> T proxy(final Class<? extends IPage> type)
throws InstantiationException, IllegalAccessException {
List<Class<?>> interfaces = Arrays.asList(type.getInterfaces());
//TODO: get interfaces properly recursively
return (T) Proxy.newProxyInstance(
type.getClassLoader(),
type.getInterfaces(),
new IPageProxy(type.newInstance(), interfaces)
);
}
#SuppressWarnings("unchecked")
public static <T extends IPage> T proxy(T object) {
//TODO: get interfaces properly recursively
List<Class<?>> interfaces = Arrays.asList(object.getClass().getInterfaces());
return (T) Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
new IPageProxy(object, interfaces)
);
}
#Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Exception {
// before method executed this code will be done
System.out.println("* Dynamic proxy invoke method executed for " + method.getName());
// Invoke original method
Object invoke = method.invoke(instance, args);
if (invoke == null) {
return null;
}
//If some of the method returns the original object
//we swap the returned object by our proxy
if (invoke == instance) {
return proxy;
}
//TODO: check if you want to swap in place
//other interfaces
if (interfaces.contains(method.getReturnType())) {
return IPageProxy.proxy((IPage)invoke);
}
return invoke;
}
}
class MyIEventDesignDialog implements IEventDesignDialog {
#Override
public IEventDesignDialog getShow() {
return null;
}
#Override
public IEventDesignDialog getEventType() {
return null;
}
#Override
public IEventDesignDialog getDateAndTimeDisplayFormat() {
return null;
}
}
Output:
* Dynamic proxy invoke method executed for getEventType
get event type method invoked successfully
* Dynamic proxy invoke method executed for getShow
get show method invoked successfully
* Dynamic proxy invoke method executed for getDateAndTimeDisplayFormat
get date and time display format method invoked successfully
* Dynamic proxy invoke method executed for getShow
You can get ideas from how Mockito is working. Please check this page: https://static.javadoc.io/org.mockito/mockito-core/2.27.0/org/mockito/Mockito.html#spy-T-
I know it is for testing, but you still can get ideas from it.
So you can apply spy() on a class and on an object to spy on it.
Related
I'm trying to create an interface for a set of classes,
but I am failing in finding a solution, which
prevents copying almost similar code for each new method in the
interface.
The interface
public interface P6BO<T extends BusinessObject> {
String[] getFields();
void create(T businessObject) throws P6BOException;
void delete(T businessObject) throws P6BOException;
}
30+ implementations of the interface.
public class ActivityBO implements P6BO<Activity> {
...
#Override
public void create(Activity activity) throws P6BOException {
...
}
#Override
public void delete(Activity activity) throws P6BOException {
...
}
}
Container class to initialize all the implementations and
provide a single point of access to each of them.
public class P6Bom {
public final ActivityBO activity = new ActivityBO();
public final EpsBO eps = new EpsBO();
public final ResourceBO resource = new ResourceBO();
public P6Bom(P6Info p6Info) throws P6BOException {
activity.activate(p6Info, p6Cache, p6Buffer);
eps.activate(p6Info, p6Cache, p6Buffer);
resource.activate(p6Info, p6Cache, p6Buffer);
}
...
public void create(BusinessObject businessObject) throws P6BOException {
if (businessObject instanceof Activity) {
activity.create((Activity) businessObject);
} else if (businessObject instanceof EPS) {
eps.create((EPS) businessObject);
} else if (businessObject instanceof Resource) {
resource.create((Resource) businessObject);
}
}
public void delete(BusinessObject businessObject) throws P6BOException {
if (businessObject instanceof Activity) {
activity.delete((Activity) businessObject);
} else if (businessObject instanceof EPS) {
eps.delete((EPS) businessObject);
} else if (businessObject instanceof Resource) {
resource.delete((Resource) businessObject);
}
}
public P6BO<? extends BusinessObject> getBO(BusinessObject businessObject) throws P6BOException {
if (businessObject instanceof Activity) {
return activity;
} else if (businessObject instanceof EPS) {
return eps;
} else if (businessObject instanceof Resource) {
return resource;
} else {
throw new P6BOException("not implemented.");
}
}
}
The test class
public class Test() {
/* Works: but is not generic (I cannot call the delete method for any BusinessObject). */
Activity activity = new Activity("MyNewActivity");
P6Bom.activity.create(activity);
P6Bom.activity.delete(activity);
/* Works: but results in a double administration in the P6Bom */
Activity activity = new Activity("MyNewActivity");
P6Bom.create(activity);
P6Bom.delete(activity);
/* Compiler error
The method delete(capture#1-of ? extends BusinessObject)
in the type P6BO<capture#1-of ? extends BusinessObject>
is not applicable for the arguments (Activity)
*/
p6Bom.getBO(activity).delete(activity);
}
What would be an elegant solution to prevent the repetitive administration of each method in the P6Bom interface?
You can use a map:
public class P6Bom {
public final Map<Class<? extends P6BO<? extends BusinessObject>>, P6BO<? extends BusinessObject>>> classMap;
public P6Bom(P6Info p6Info) throws P6BOException {
classMap = new HashMap<>();
classMap.put(ActivityBo.class, new ActivityBO());
...
for(var bo: classMap.values()) {
bo.activate(p6Info, p6Cache, p6Buffer);
}
}
...
public void create(BusinessObject businessObject) throws P6BOException {
getBO(businessObject).create(businessObject);
}
public P6BO<? extends BusinessObject> getBO(BusinessObject businessObject) throws P6BOException {
if (classMap.containsKey(businessObject.getClass())) {
return classMap.get(businessObject.getClass());
} else {
throw new P6BOException("not implemented.");
}
}
}
I forget if the <? extends BusinessObject> allows you to do that. If the compiler complains, omit that part in the classMap declaration and the getBO signature and ignore the warnings ;-)
The thing to remember here is that in the bytecode, the generics are removed, so the actual method signature allows you always to pass in BusinessObjects. Once inside the method, it will be cast and throw a RuntimeException if the class doesn't match, but from the compiler/interface side, it is perfectly possible to write code like that.
This seems like a classical factory and strategy pattern issue, where the Open/Closed principle is violated. You want to extend your application, but without modifying it.
#kutschkem's solution is so far the most elegant way. In addition I would suggest to instantiate the strategy/business object manager when the first business object is created.
This will free your hands from enumerating the BOM's, because the suggested way, the Open/Closed principle is still violated. Once a new business object is introduced, its respective manager needs to be added in the class.
The solution could be the following:
public class P6Bom {
private final Map<String, P6BO<? extends BusinessObject>> classMap;
private final P6Info info;
private final P6Cache cache;
private final P6Buffer buffer;
public P6Bom(P6Info info, P6Cache cache, P6Buffer buffer) {
this.classMap = new HashMap<>();
this.info = info;
this.cache = cache;
this.buffer = buffer;
}
public <T extends BusinessObject> void create(T businessObject) throws P6BOException {
this.getBO(businessObject).create(businessObject);
}
public <T extends BusinessObject> P6BO<T> getBO(T businessObject) throws P6BOException {
if (!this.classMap.containsKey(businessObject.getClass().getName())) {
try {
var bo = (P6BO<T>)Class.forName(businessObject.getClass().getPackageName() + ".bo." + businessObject.getClass().getSimpleName() + "BO").getConstructors()[0].newInstance();
bo.activate(this.info, this.cache, this.buffer)
this.classMap.put(
businessObject.getClass().getName(),
bo
);
} catch (Exception e) {
throw new P6BOException("not implemented.");
}
}
return (P6BO<T>)this.classMap.get(businessObject.getClass().getName());
}
}
Of course, for that solution there need to be a convention (or configuration) how strategies (e.g. ActivityBO) could be found for BusinessObjects. In the example, they live in a subpackage called bo.
Is there an some API like Proxy.getProxiedObject(), that will return the original object of a dynamic proxy? I would like, for example, to invoke equals on the proxied objects, rather than on the dynamic proxies themselves, like in the following example:
public class ProxyTest implements InvocationHandler {
public static Object createProxy(Object target) {
Class<? extends Object> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new ProxyTest());
}
public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
// PROXIED OBJECTS COMPARISON - DESIRED
// return Proxy.getProxiedObject(proxy).equals(Proxy.getProxiedObject(args[0]));
// DYNAMIC PROXIES COMPARISON - UNDESIRED
// return proxy.equals(args[0]);
return null;
}
public static void main(String[] args) {
Object proxied = createProxy(new Object());
System.out.println(proxied.equals(proxied));
}
}
I don't think there's any API available for this; but I built a workaround this using the API that retrieves the InvocationHandler from any Proxy object, and the one that tests where or not a Class is a Proxy class:
InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException
boolean isProxyClass(Class<?> cl)
Using these I created an abstract extension of InvocationHandler to keep a reference to the object being proxied, with a static utility to retrieve the proxied object out of any potential Proxy object, and a factory utility for creating Proxys using the target object:
public abstract class ProxiedSavedInvocationHandler implements InvocationHandler {
public static Object getProxied(Object proxy) {
if (!Proxy.isProxyClass(proxy.getClass()))
return null;
InvocationHandler handler = Proxy.getInvocationHandler(proxy);
return (handler instanceof ProxiedSavedInvocationHandler) ?
((ProxiedSavedInvocationHandler)handler).proxied : null;
}
protected final Object proxied;
public ProxiedSavedInvocationHandler(Object proxied) {
this.proxied = proxied;
}
public Object getProxied() {
return proxied;
}
public Object createProxy() {
Class<? extends Object> clazz = proxied.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
}
Then I just used the newly created class like this:
class MyProxiedSavedInvocationHandler extends ProxiedSavedInvocationHandler {
...
}
ProxiedSavedInvocationHandler handler = new MyProxiedSavedInvocationHandler(target);
Object proxy = handler.createProxy();
// DESIRED API THROUGH STATIC UTILIY
Object proxied1 = ProxiedSavedInvocationHandler.getProxied(proxy);
// DESIRED API THROUGH INSTANCE UTILIY
Object proxied2 = handler.getProxied();
The only dependency on this solution is having the ProxiedSavedInvocationHandler utility class where all the logic and new APIs are located. This class can be extended even to include APIs to delegate behavior to other InvocationHandlers transparently; but the minimum required is there.
The following is a full working example application of this solution:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyTest {
static class MyProxiedSavedInvocationHandler extends ProxiedSavedInvocationHandler {
public MyProxiedSavedInvocationHandler(Object proxied) {
super(proxied);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
if (!method.getName().equals("equals"))
return method.invoke(proxied, args);
Object other = ProxiedSavedInvocationHandler.getProxied(args[0]);
System.out.println("====");
System.out.println("\tRunning 'equals' inside proxy with:");
System.out.println("\tthis: " + proxied);
System.out.println("\tother: " + other);
System.out.println("====");
return proxied.equals(other);
}
}
static abstract class ProxiedSavedInvocationHandler implements InvocationHandler {
public static Object getProxied(Object proxy) {
if (!Proxy.isProxyClass(proxy.getClass()))
return null;
InvocationHandler handler = Proxy.getInvocationHandler(proxy);
return (handler instanceof ProxiedSavedInvocationHandler) ?
((ProxiedSavedInvocationHandler)handler).proxied : null;
}
protected final Object proxied;
public ProxiedSavedInvocationHandler(Object proxied) {
this.proxied = proxied;
}
public Object getProxied() {
return proxied;
}
public Object createProxy() {
Class<? extends Object> clazz = proxied.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
}
// TO TEST EDGE SCENARIONS
private static Object createProxy(Class<? extends Object> clazz, InvocationHandler handler) {
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), handler);
}
// MAIN
public static void main(String[] args) {
// EDGE SCENARIOS
Object proxiedFromNotEnhancedProxy =
ProxiedSavedInvocationHandler.getProxied(createProxy(Object.class, (p, m, a) -> null));
Object proxiedFromNotAProxy =
ProxiedSavedInvocationHandler.getProxied(new Object());
System.out.println("proxied from NOT ENHANCED PROXY: " + proxiedFromNotEnhancedProxy);
System.out.println("proxied from NOT A PROXY: " + proxiedFromNotAProxy);
System.out.println();
// FUNCTIONALITY DESIRED
Object target = new Object();
ProxiedSavedInvocationHandler handler = new MyProxiedSavedInvocationHandler(target);
Object proxy = handler.createProxy();
Object proxied1 = ProxiedSavedInvocationHandler.getProxied(proxy);
Object proxied2 = handler.getProxied();
System.out.println("target: " + target);
System.out.println("proxied1: " + proxied1);
System.out.println("target == proxied1: " + (target == proxied1));
System.out.println("proxy.equals(proxy): " + proxy.equals(proxy));
}
}
Complete code on GitHub
This question already has answers here:
Create instance of generic type in Java?
(29 answers)
Closed 5 years ago.
I want to create an instance just by defining the type for a generic class
public abstract class Base<T> {
private final T genericTypeObject;
protected Base(){
//Create instance of T here without any argument
}
}
So that I just can call the default constructor:
public class Child extends Base<SomeClass>{
public Child () {
super();
}
}
and the Base-class implementation will create me an instance of the GenericType.
The generic information will be erased at compile time, so there will be no T anymore during runtime (you loose the information). Thats why you somewhere will need a Class<> to store the information.
The most clean & simple solution form my point of view is to pass in the class to to the constructor. I know you requested it to be without any constructor argument, but I do not think this is possible.
Code Sample
public abstract class AbstractBase<T> {
private final T genericTypeObject;
protected Base(Class<T> type){
try {
genericTypeObject = type.newInstance();
} catch (InstantiationException e) {
// Handle
} catch (IllegalAccessException e) {
// Handle
}
}
}
public class Child extends Base<SomeClass> {
public Child () {
super(SomeClass.class);
}
}
Alternative Solution
Using a Supplier (thanks for the comment #Jorn Vernee):
public abstract class AbstractBase<T> {
private final T genericTypeObject;
public AbstractBase(Supplier<T> supplier) {
genericTypeObject = supplier.get();
}
}
public class Child extends AbstractBase<SomeClass> {
public Child() {
super(SomeClass::new);
}
}
You must have access to the class to create an instance, so creating an instance without an argument is not possible. You must pass a Class<T>.
UPDATE:
See #JDC's answer.
At runtime this returns me a Class instance:
public static Class<?> getGenericClassOfType(Object object){
Class<?> clazz = (Class<?>) ((ParameterizedType) object.getClass() .getGenericSuperclass()).getActualTypeArguments()[0];
return clazz;
}
and afterwards I can initiate it with:
public static <T> T getDefaultInstance(Class<T> clazz) throws IllegalAccessException, InvocationTargetException, InstantiationException {
T instance = null;
Constructor<T>[] constructors = (Constructor<T>[]) clazz.getDeclaredConstructors();
Constructor<T> constructor = null;
for (Constructor cstr : constructors) {
//Only if default constructor
if (cstr.getParameters().length == 0) {
constructor = (Constructor<T>) cstr;
break;
}
}
if (constructor != null) {
constructor.setAccessible(true);
instance = constructor.newInstance();
}
return instance;
}
so the code in my base constructor looks like:
public abstract class BaseScene<T extends SceneController> {
private final static Logger LOGGER = LogManager.getLogger(BaseScene.class);
private final T sceneController;
//public T getSceneController() {
// return sceneController;
//}
protected BaseScene(){
T newInstance = null;
try {
Class<T> clazz = (Class<T>)ReflectionHelper.getGenericClassOfType(this);
newInstance = ReflectionHelper.getDefaultInstance(clazz);
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
LOGGER.error("Error while trying to initiate BaseScene",e);
}
sceneController = newInstance;
}
}
which works perfectly as I tested it.
I have an interface which is implemented by few classes. Based on the full name of the class I want to initialize the class objects.
Interface,
public interface InterfaceSample{
}
Class files,
public class ABC implements InterfaceSample{
}
public class XYZ implements InterfaceSample{
}
A sample test class,
public class SampleManager{
public static InterfaceSample getInstance(String className) {
InterfaceSample instance = null;
try {
instance = (InterfaceSample) Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return instance;
}
}
I am getting the following error,
"Cannot cast from Class<capture#1-of ?> to InterfaceSample"
How can I initialize the classes based on its name.
You're almost there:
instance = (InterfaceSample) Class.forName(className).newInstance();
remember to mark the method with:
throws Exception
because newInstance() is marked so as well (it throws InstantiationException and IllegalAccessException to be precise).
You must invoke newInstance() on the class to get an instance.
public class SampleManager{
public static InterfaceSample getInstance(String className) throws Exception {
InterfaceSample instance = null;
try {
instance = (InterfaceSample) Class.forName(className).newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return instance;
}
}
So I have a few 'Manager' classes, for example GroupManager. All these Managers are singletons.
Using this method for instancing:
private static GroupManager groupManager = null;
private GroupManager()
{
}
public static GroupManager Instance()
{
if (groupManager == null)
{
groupManager = new GroupManager();
}
return groupManager;
}
I'm thinking I should start to use some inheritance as they have a lot of copied methods.
The Instance() methods for each Manager is the same.
So for inheritance i can do this (obviously):
GroupManager extends Manager
Is it possible to use generics to use the same Instance method for all managers, something like:
public class Manager<E>
{
private static E instance = null;
public static E Instance()
{
if (instance == null)
{
instance = new E();
}
return instance;
}
}
I think that makes sense :)
So then you would do GroupManager.Instance() like normal.
You don't understand how generics and statics work. If you have a static field or method (such as "instance" or instance()), which can be called without instantiating the class Manager, how do you expect the JVM (and the compiler even) to know what type E is supposed to be?
Here's an example, as per G_H's suggestion:
GeneralManager and AreaManager both extend Manager
The Manager class is the only one that has the getInstance() static method:
public class Manager {
private static Map<Class<? extends Manager>,Manager> INSTANCES_MAP = new java.util.HashMap<Class<? extends Manager>, Manager>();
//Also, you will want to make this method synchronized if your application is multithreaded,
//otherwise you mihgt have a race condition in which multiple threads will trick it into
//creating multiple instances
public static <E extends Manager> E getInstance(Class<E> instanceClass) throws InstantiationException, IllegalAccessException {
if(INSTANCES_MAP.containsKey(instanceClass)) {
return (E) INSTANCES_MAP.get(instanceClass);
} else {
E instance = instanceClass.newInstance();
INSTANCES_MAP.put(instanceClass, instance);
return instance;
}
}
}
Nope, it's not gonna work. Java uses generics at compile time for type checking, but doesn't generate extra classes or retain info regarding type parameters at runtime.
When you declare Manager<E> with that type parameter E, that's something that will only play a role in an actual instance. You could have a subclass like GroupManager extends Manager<String> or whatever, but that's not magically gonna generate a variety of the static method.
Static methods and members belong with a class, not an instance. So trying to use generics there, which are intended for typing instances, isn't gonna fly.
If you make your group manager class as follows then you can call your instance method.
public class GroupManager extends Manager<GroupManager>{}
And in your Manager class try this...
public class Manager<E>
{
private static E instance = null;
public static E Instance()
{
try {
return instance.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
Or if you know the object you want an instance for, just make the method generic
public static <T> T getInstance(Class<T> t){
try {
return t.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
I havn't tried any of this so not sure if it will work.
Injecting the constructor in a generic context. Cash is not thread safe, but is only used in static context so its fine if you don't miss use it
public class Example {
public static class MySingletonClass {
}
public interface Provider<T> {
T get();
}
static final Provider<MySingletonClass> myClassInstanceProvider = new Cash<MySingletonClass>(new Provider<MySingletonClass>() {
#Override
public MySingletonClass get() {
return new MySingletonClass();
}
});
public static class Cash<T> implements Provider<T> {
private Provider<T> provider;
public Cash(Provider<T> provider) {
this.provider = provider;
}
#Override
public T get() {
final T t = provider.get();
provider = new Provider<T>() {
#Override
public T get() {
return t;
}
};
return t;
}
}
}
public class Manager<E>{
private static Object instance = null;
public static E Instance() {
if (instance == null)
{
instance = new E();
}
return (E)instance;
}
}