I saw similar questions here on SO, but, I am asking this question based on this.
I have implemented the suggestion given by the accepted answer but, still I see two instances being created. The goal is that I want an instance to be created only on a particular method call. I can't use static method in an interface with Java 6.
The code I have tried
private static final Map<String, IRule> instancesMap = new Hashtable<String, IRule>();
#SuppressWarnings("unchecked")
public static <T extends IRule> T getRuleInstance(String clazz) {
try {
if (isInstanceCreated(clazz)) {
T type = (T) instancesMap.get(clazz);
if (logger.isDebugEnabled()) {
logger.debug("Found a cashed instance of " + clazz + ". Returning " + type);
}
return type;
} else {
Class<?> ruleObject = Class.forName(clazz);
Constructor<?> clazzConstructor = ruleObject.getDeclaredConstructor();
/**
* Hack encapsulation
*/
clazzConstructor.setAccessible(true);
IRule iRuleInstance = (IRule) clazzConstructor.newInstance();
if (logger.isDebugEnabled()) {
logger.debug("The instance of class " + clazz + " " + iRuleInstance + " has been created.");
}
instancesMap.put(clazz, iRuleInstance);
return (T) iRuleInstance.getInstance();
}
} catch (ClassNotFoundException e) {
logger.error("ClassNotFoundException", e);
} catch (IllegalAccessException e) {
logger.error("IllegalAccessException", e);
} catch (SecurityException e) {
logger.error("SecurityException", e);
} catch (NoSuchMethodException e) {
logger.error("NoSuchMethodException", e);
} catch (IllegalArgumentException e) {
logger.error("IllegalArgumentException", e);
} catch (InvocationTargetException e) {
logger.error("InvocationTargetException", e);
} catch (InstantiationException e) {
logger.error("InstantiationException", e);
}
return null;
}
private static boolean isInstanceCreated(String clazz) {
return instancesMap.containsKey(clazz);
}
At the moment if there's a cache hit, you return the contents of the cache (instancesMap.get(clazz)). But if no hit, you cache one thing (instancesMap.put(clazz, iRuleInstance)) and return another (iRuleInstance.getInstance()). That doesn't make sense.
Don't call getInstance after adding to map, just return it:
instancesMap.put(clazz, iRuleInstance);
return (T) iRuleInstance;
Or, do call getInstance, but cache it:
(T) instance = iRuleInstance.getInstance();
instancesMap.put(clazz, instance);
return (T) instance;
Either way you must return what you cache so that it matches your logic for a cache hit.
Is there a particular reason why you need to have the concrete implementation of IRule?-- what methods are you going to be using from the implementation? Can't they be generalised into the interface itself? If this is the case, you could just have a map of Map<Class<? extends IRule>, IRule> to manage your instances.
Related
Is there a way to do the following? Check if a class exists (in the same package) and if it does exist, check if a particular method exists, and if so, calling it?
Say that I have class X. In some method of class X, I want to do the following:
if (class Y exists) { //Maybe use Class.forName("Y")?
if ( Y has method a(String, String) ) {
call Y.a("hello", "world");
}
}
Is such a thing possible? And is doing such a thing reasonable? Thanks.
Is such a thing possible? And is doing such a thing reasonable?
Thanks.
Of course it is possible.
If you develop a program or a library that has to discover dynamically some classes, it is a very reasonable thing.
If it is not the case, it could not be.
If your need makes sense, you should ask you an additional question : should you invoke a static or instance method ?
Here is a sample example with both solutions :
ReflectionClass that contains the logic using reflection :
import java.lang.reflect.Method;
public class ReflectionCalls {
public static void main(String[] args) {
new ReflectionCalls();
}
public ReflectionCalls() {
callMethod(true);
callMethod(false);
}
private void callMethod(boolean isInstanceMethod) {
String className = "DiscoveredClass";
String staticMethodName = "methodStatic";
String instanceMethodName = "methodInstance";
Class<?>[] formalParameters = { int.class, String.class };
Object[] effectiveParameters = new Object[] { 5, "hello" };
String packageName = getClass().getPackage().getName();
try {
Class<?> clazz = Class.forName(packageName + "." + className);
if (!isInstanceMethod) {
Method method = clazz.getMethod(staticMethodName, formalParameters);
method.invoke(null, effectiveParameters);
}
else {
Method method = clazz.getMethod(instanceMethodName, formalParameters);
Object newInstance = clazz.newInstance();
method.invoke(newInstance, effectiveParameters);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
DiscoveredClass (the class we manipulate in the example)
package reflectionexp;
public class DiscoveredClass {
public static void methodStatic(int x, String string) {
System.out.println("static method with " + x + " and " + string);
}
public void methodInstance(int x, String string) {
System.out.println("instance method with " + x + " and " + string);
}
}
Output :
instance method with 5 and hello
static method with 5 and hello
Yes, this can be done. I've created a Test class in the same Package as the current class.
import java.lang.reflect.Method;
public class Sample {
public static void main(String[] args) {
Class<?> clazz = null;
try {
clazz = Class.forName("Test");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (clazz == null) {
System.out.println("class not found. Go eat some waffles and correct the name");
return;
}
Method m = null;
try {
m = clazz.getMethod("foo", null);
} catch (NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (m == null) {
System.out.println("method not found. Go eat some waffles and correct the name");
return;
}
Test t;
try {
t = (Test) clazz.newInstance();
m.invoke(t, null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class Test {
static {
System.out.println("test...");
}
public void foo() {
System.out.println("foo");
}
}
O/P :
test...
foo
You can use Class.forName:
try {
Class yourClass = Class.forName( "classname" );
Object o = yourClass.newInstance();
} catch( ClassNotFoundException e ) {
//Throw error or whatever
}
To check if a method exists you could use the NoSuchMethodError e in a try/catch
You can do this using reflection, however it isnt really practical unless you are trying to access classes that potentially will not be present at runtime or if you are trying to access private or hidden fields. Example below.
public static void reflectionDemo(){
//Here we attempt to get the common URI class
//If it is found, we attempt to get the create method
//We then invoke the create method and print the class name of the result.
try {
Class<?> uriClass = Class.forName("java.net.URI");
//getMethod(String name, Class<?>... args);
java.lang.reflect.Method create = uriClass.getMethod("create", String.class);
//The first parameter is null because this is a static method.
//invoke(Object target, Object... args);
System.out.println(create.invoke(null, "some/uri").getClass());
//Will print class java.net.URI
} catch (ClassNotFoundException e) {
// If class doesnt exist
e.printStackTrace();
} catch (NoSuchMethodException e) {
// If method doesnt exist
e.printStackTrace();
} catch (SecurityException e) {
// See Javadoc
e.printStackTrace();
} catch (IllegalAccessException e) {
// From invoke
e.printStackTrace();
} catch (IllegalArgumentException e) {
// From invoke
e.printStackTrace();
} catch (java.lang.reflect.InvocationTargetException e) {
// From invoke
e.printStackTrace();
}
}
To find whether a class exists, you can use the forName() method on Class.
To find whether a method exists, you can use the getMethod() method on Class.
Documentation here:
https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#forName(java.lang.String)
https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getMethod(java.lang.String,%20java.lang.Class...)
For your class problem, you'd want to use code like:
try {
Class.forName("Y");
}
catch (ClassNotFoundException e) {
}
For your method problem, you'd want to use code like:
try {
Class.getMethod(a);
}
catch (NoSuchMethodException e) {
}
You can check if the Class exists with Class.forName("classname");
See this SO question: Check if class exists somewhere in package
If a method exists can be catched with NoSuchMethodError in your try/catch.
See this SO question: Check if method exists at Runtime in Java
try {
Object object = Class.forName("Y").newInstance();
object.a(String, String);
} catch( ClassNotFoundException | NoSuchMethodError ex) {
//do Something else
}
I want to create an instance of a class, but I also need to initialize also all its fields recursively.
The code you see related do the objectFactory is because some of this classes could be JAXB classes, so for every package there is an ObjectFactory with methods like createJaxbObject(....).
EDITED:
My final solutions is this one:
public Object getInstance(Class<?> instanceClass, Boolean simple,
String jaxbName) {
Object instance = null;
try {
if (instanceClass.isPrimitive())
return primitiveValues.get(instanceClass.getName());
if (List.class.isAssignableFrom(instanceClass))
return new ArrayList();
else if (instanceClass.isEnum())
return instanceClass.getEnumConstants()[0];
else if (instanceClass.isArray())
return java.lang.reflect.Array.newInstance(instanceClass, 1);
else if (BigInteger.class.isAssignableFrom(instanceClass))
return new BigInteger("0");
else if (instanceClass.equals(String.class))
return "";
else if (instanceClass.equals(Boolean.class))
return false;
else if (instanceClass.equals(EntityObjectStringType.class))
return new EntityObjectStringType();
else if (JAXBElement.class.isAssignableFrom(instanceClass)) {
try {
Method m = null;
Class<?> objFactoryClass = null;
Iterator<String> it = EditorServlet.objectFactories
.iterator();
Object of = null;
while (it.hasNext()) {
objFactoryClass = Class.forName(it.next());
of = objFactoryClass.getConstructor().newInstance();
m = getMethodFromObjectFactory(objFactoryClass,
jaxbName);
if (m != null)
if (m.getParameterTypes().length > 0)
break;
}
Object jaxbElement = getInstance(m.getParameterTypes()[0],
m.getParameterTypes()[0].getSimpleName());
return m.invoke(of, jaxbElement);
} catch (NoSuchMethodException e) {
logger.error("JAXB NoSuchMethodException");
}
} else
try {
logger.info("Costruttori per " + instanceClass.getName()
+ " " + instanceClass.getConstructors().length);
instance = instanceClass.getConstructor().newInstance();
} catch (NoSuchMethodException noSuchMethodException) {
logger.error("getConstructors NoSuchMethodException");
}
} catch (IllegalArgumentException e) {
logger.error("IllegalArgumentException " + instanceClass.getName());
} catch (SecurityException e) {
logger.error("SecurityException " + instanceClass.getName());
} catch (InstantiationException e) {
logger.error("InstantiationException " + instanceClass.getName()
+ " " + instanceClass.isPrimitive());
} catch (IllegalAccessException e) {
logger.error("IllegalAccessException " + instanceClass.getName());
} catch (InvocationTargetException e) {
logger.error("InvocationTargetException " + instanceClass.getName());
} catch (ClassNotFoundException e) {
logger.error("ClassNotFoundException " + instanceClass.getName());
}
if (!simple) {
for (Field field : instanceClass.getDeclaredFields()) {
try {
Object fieldInstance = getInstance(field.getType(),
field.getName());
field.setAccessible(true);
field.set(instance, fieldInstance);
} catch (IllegalArgumentException e) {
logger.error("IllegalArgumentException "
+ instanceClass.getName());
} catch (IllegalAccessException e) {
logger.error("IllegalAccessException "
+ instanceClass.getName());
}
}
}
return instance;
}
If I could hazard a guess, you're calling your method recursively in your NoSuchMethodException catch.
Object of = getInstance(objFactoryClass);
If your recursive call keeps on not finding the method on:
Method m = getMethodFromObjectFactory(objFactoryClass, c);
... the method will call itself again, which should end with a StackOverflowError at some point.
Your recursion has no break point
Try to stop recursion where the class is primary type:
if (List.class.isAssignableFrom(c))
instance = new ArrayList();
else if (c.isEnum())
return c.getEnumConstants()[0]; //avoid stackoverflow error
else if(c.isPrimitive()) {
instance = c.getConstructor().newInstance();
// use must stop here
return instance;
} else{
instance = c.getConstructor().newInstance();
}
The isPrimitive will judge whether the class is primary type(int ,Integer,shor,Short,String...)
I am building a Widget which contains a ProgressBar. If the Widget is computing I set the visibility of that ProgressBar to VISIBLE, and to INVISIBILE if all computings stopped. There should be no Problem, because the setVisibility is documented as RemotableViewMethod. However some guys at HTC seem to forget it, (i.e on the Wildfire S), so a call to RemoteViews.setVisibility will result in a crash. For this reason I try to implement a check, if setVisibility is really callable. I have writen this Method for it:
private boolean canShowProgress(){
LogCat.d(TAG, "canShowProgress");
Class<ProgressBar> barclz = ProgressBar.class;
try {
Method method = barclz.getMethod("setVisibility", new Class[]{int.class});
Annotation[] anot = method.getDeclaredAnnotations();
return anot.length > 0;
} catch (SecurityException e) {
LogCat.stackTrace(TAG, e);
} catch (NoSuchMethodException e) {
LogCat.stackTrace(TAG, e);
}
return false;
}
This will work, but is REALLY ugly as it will return `True´ if ANY Annotiation is present. I looked, how RemoteView itself is doing the lookup and found this:
if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
throw new ActionException("view: " + klass.getName()
+ " can't use method with RemoteViews: "
+ this.methodName + "(" + param.getName() + ")");
}
But i could't do the same, because the Class RemotableViewMethod is not accsesible through the sdk. How to know if it is accesible or not?
By Writing my question I had the Idea to lookup for the class by its Name, and it worked.
So I updated my Method to the following:
private boolean canShowProgress(){
LogCat.d(TAG, "canShowProgress");
Class<ProgressBar> barclz = ProgressBar.class;
try {
Method method = barclz.getMethod("setVisibility", new Class[]{int.class});
Class c = null;
try {
c = Class.forName("android.view.RemotableViewMethod");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return (this.showProgress= (c != null && method.isAnnotationPresent(c)));
} catch (SecurityException e) {
LogCat.stackTrace(TAG, e);
} catch (NoSuchMethodException e) {
LogCat.stackTrace(TAG, e);
}
return false;
}
which works flawlessly
I am trying to set a number of Enums to default value I am using the following method:
private void checkEnum(Field field, String setMethod) {
// TODO Auto-generated method stub
try {
String className = Character.toUpperCase(field.getName().charAt(0)) +
field.getName().substring(1);
Class<?> cls = Class.forName("com.citigroup.get.zcc.intf." + className);
Object[] enumArray = cls.getEnumConstants();
//set to the last Enum which is unknown
invoke(setMethod, enumArray[enumArray.length - 1] );
} catch(Exception e) {
System.out.println(e.toString());
}
}
The problem is actually setting the Enum. I have extracted the enum type but to then call the MethodInvoker. Passing in the Enum object is proving a problem. All the enums have the following as the last element of the enum array.
EnumName.UNKNOWN
However this is not being set via the invoke method which looks like:
private Object invoke(String methodName, Object newValue) {
Object value = null;
try {
methodInvoker.setTargetMethod(methodName);
if (newValue != null) {
methodInvoker.setArguments(new Object[]{newValue});
} else {
methodInvoker.setArguments(new Object[]{});
}
methodInvoker.prepare();
value = methodInvoker.invoke();
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(),e);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(),e);
} catch (java.lang.reflect.InvocationTargetException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(),e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(),e);
}
return value;
}
So I'm lost as to why the
invoke(setMethod, enumArray[enumArray.length -1] );
Is not setting my Enum
I attempted to get your code running. The methodInvoker.prepare() call was throwing:
java.lang.IllegalArgumentException: Either 'targetClass' or 'targetObject' is required
So I added in the class missing parameter and the code works, if I understand your use case.
You appear to be setting a static field whose name must be the name of an Enum class under com.citigroup.get.zcc.intf with the first character in the field name downcased.
Here is my modified code:
public void checkEnum(Field field, String setMethod, Class clazz) {
try {
String className = Character.toUpperCase(field.getName().charAt(0)) +
field.getName().substring(1);
Class<?> cls = Class.forName("com.citigroup.get.zcc.intf." + className);
Object[] enumArray = cls.getEnumConstants();
//set to the last Enum which is unknown
invoke(setMethod, enumArray[enumArray.length - 1], clazz);
} catch (Exception e) {
System.out.println(e.toString());
}
}
private Object invoke(String methodName, Object newValue, Class clazz) {
Object value = null;
try {
MethodInvoker methodInvoker = new MethodInvoker(); // this was missing
methodInvoker.setTargetMethod(methodName);
methodInvoker.setTargetClass(clazz); // This was missing
if (newValue != null) {
methodInvoker.setArguments(new Object[]{newValue});
} else {
methodInvoker.setArguments(new Object[]{});
}
methodInvoker.prepare();
value = methodInvoker.invoke();
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(), e);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(), e);
} catch (java.lang.reflect.InvocationTargetException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(), e);
}
return value;
}
}
My test code resembled (Show is an enum class of mine, MethodNameHelper has been previously posted to StackExchange):
public class StackExchangeTestCase {
protected static final Logger log = Logger.getLogger(StackExchangeTestCase.class);
public static Show show;
public static void setShow(Show newShow) {
show = newShow;
}
#Test
public void testJunk() throws Exception {
Method me = (new Util.MethodNameHelper(){}).getMethod();
Class<?> aClass = me.getDeclaringClass();
Field att1 = aClass.getField("show");
show = null;
methodNameHelper.checkEnum(att1, "setShow", aClass);
System.out.println(show); // worked
}
}
If I have 2 classes, "A" and "B", how can I create a generic factory so I will only need to pass the class name as a string to receive an instance?
Example:
public static void factory(String name) {
// An example of an implmentation I would need, this obviously doesn't work
return new name.CreateClass();
}
Thanks!
Joel
Class c= Class.forName(className);
return c.getDeclaredConstructor().newInstance();//assuming you aren't worried about constructor .
javadoc
For invoking constructor with argument
public static Object createObject(Constructor constructor,
Object[] arguments) {
System.out.println("Constructor: " + constructor.toString());
Object object = null;
try {
object = constructor.newInstance(arguments);
System.out.println("Object: " + object.toString());
return object;
} catch (InstantiationException e) {
//handle it
} catch (IllegalAccessException e) {
//handle it
} catch (IllegalArgumentException e) {
//handle it
} catch (InvocationTargetException e) {
//handle it
}
return object;
}
}
have a look
You may take a look at Reflection:
import java.awt.Rectangle;
public class SampleNoArg {
public static void main(String[] args) {
Rectangle r = (Rectangle) createObject("java.awt.Rectangle");
System.out.println(r.toString());
}
static Object createObject(String className) {
Object object = null;
try {
Class classDefinition = Class.forName(className);
object = classDefinition.newInstance();
} catch (InstantiationException e) {
System.out.println(e);
} catch (IllegalAccessException e) {
System.out.println(e);
} catch (ClassNotFoundException e) {
System.out.println(e);
}
return object;
}
}