I recently asked a question about some code of mine where I used reflection and one who wanted to help me with this problem mentioned that I shouldn't use reflection like this and that there is a better way doing it.
So I have an Interface for searching in external Systems:
public interface ReferenceController {
public Map<String, ReferenceElement> searchElements(String searchField, List<String> searchItems, SystemStage systemStage) throws Exception;
public String getStateMapping(String value);
public Boolean isAvailable(SystemStage systemStage) throws Exception;
}
And I have an ENUM where I declare which external systems I have and how their class is named which uses this interface. So if any other programmer want's to implement a new external system he has only to fill the interface and put two values in this ENUM and tada it should work.
So the part where I used the reflection was
public static void performSingleSearch(ReferenceSystem referenceSystem, String searchField, List<String> searchValues, SystemStage systemStage) throws Exception {
if(!isAvailable(referenceSystem, systemStage)) return;
Map<String, ReferenceElement> result = new HashMap<>();
try {
Class<?> classTemp = Class.forName(referenceSystem.getClassname());
Method method = classTemp.getMethod("searchElements", String.class , List.class, SystemStage.class);
result = (Map<String, ReferenceElement>) method.invoke(classTemp.newInstance(), searchField, searchValues, systemStage);
} catch (Exception e) {
return;
}
if(result != null) orderResults(result, referenceSystem);
}
In the ENUM ther is a function getClassname, which answers with the fqcn.
The Enum looks like this:
public enum ReferenceSystem {
UCMDB (refSystems.ucmdb.UcmdbFunctions.class),
PROIPS (refSystems.proips.ProIPSFunctions.class),
KV (refSystems.kv.KvFunctions.class),
FISERVICE(refSystems.fiservice.FiServiceFunctions.class),
COMMAND (refSystems.command.CommandFunctions.class),
FII (refSystems.fii.FiiFunctions.class);
private Class<?> clazz;
private ReferenceSystem(Class<?> controllerClass) {
this.clazz = controllerClass;
}
public String displayName() {
ResourceBundle bundle = ResourceBundle.getBundle("EnumI18n", Locale.GERMAN);
return bundle.getString(toString());
}
public String localizedDisplayName(Locale locale) {
ResourceBundle bundle = ResourceBundle.getBundle("EnumI18n", locale);
return bundle.getString(toString());
}
public Class<?> getClassname() { return clazz; }
}
I've already altered it according to #jhamon 's answer.
But I get an error when I try
classTemp.newInstance().searchElemets(...)
Because it doesn't know about searchElemts().
So the other user here said there would be the possibility of implementing the interface into the enum and then I don't have to reflect.
Could anyone tell me how, because I don't know and I don't know where or what to search.
Thanks
It seems all your search engines have a common method searchElementsand it's defined in the interface
Knowing that, why not call this method directly, and not by looking for it first. -> no more reflection to find the method.
public interface ReferenceController {
public Map<String, ReferenceElement> searchElements(String searchField, List<String> searchItems, SystemStage systemStage) throws Exception;
public String getStateMapping(String value);
public Boolean isAvailable(SystemStage systemStage) throws Exception;
}
Instead of storing the class name as String in the Enum, store the .class -> no more reflection to find the class.
public static void performSingleSearch(ReferenceSystem referenceSystem, String searchField, List<String> searchValues, SystemStage systemStage) throws Exception {
if(!isAvailable(referenceSystem, systemStage)) return;
Map<String, ReferenceElement> result = new HashMap<>();
try {
Class<?> classTemp = referenceSystem.getClazz();
result = ((ReferenceController) classTemp.newInstance()).searchElements(searchField, searchValues, systemStage);
} catch (Exception e) {
return;
}
if(result != null) orderResults(result, referenceSystem);
}
Related
I'm having a problem in writing an algorithm to help me scan a file system and find all subclasses of a certain class.
Details:
I've an app that scans an external application using nio Files.walk() while retrieving I check for "extends SuperClass" while reading the file if the word exits, I add the class name in my list as follows:
List<String> subclasses = new ArrayList<>();
Files.walk(appPath)
.filter(p->Files.isRegularFile(p) && p.toString()
.endsWith(".java")).forEach(path -> {
try {
List<String> lines = Files.readAllLines(path);
Pattern pattern = Pattern.compile("\\bextends SuperClass\\b");
Matcher matcher = pattern
.matcher(lines.stream()
.collect(Collectors.joining(" ")));
boolean isChild = matcher.find();
if(isChild) subclasses.add(path.getFileName().toString());
}catch (IOException e){
//handle IOE
}
The problem with the above is that it only gets direct subclasses of SuperClass but I need to retrieve all direct and indirect subclasses.
I thought about recursion since I've no Idea how many subclasses of SuperClass there is but I couldn't implement any reasonable implementation.
NOTES:
Scanning more than 600 thousands file
I have no Idea how many direct/indirect subclasses of SuperClass there is
The application that I'm scanning is external and I can't modify its code so I'm only allowed to access it by reading files and see where extends exists
If there is a non-recursive solution to the problem that would be great but if there's no other way, I'll be more than happy to accept a recursive one since I care about the solution more than performance.
Edit:
I use the following regex to compare both name and import to make sure even in case of same name different packages the output is correct:
Pattern pattern = Pattern.compile("("+superClasss.getPackage()+")[\\s\\S]*(\\bextends "+superClass.getName()+"\\b)[\\s\\S]");
I also tried:
Pattern pattern = Pattern.compile("\\bextends "+superClass.getName()+"\\b");
But there is also some missing subclasses, I believe that the code bellow skips some checks, and doesn't fully work:
public static List<SuperClass> getAllSubClasses(Path path, SuperClass parentClass) throws IOException{
classesToDo.add(baseClass);
while(classesToDo.size() > 0) {
SuperClass superClass = classesToDo.remove(0);
List<SuperClass> subclasses = getDirectSubClasses(parentPath,parentClass);
if(subclasses.size() > 0)
classes.addAll(subclasses);
classesToDo.addAll(subclasses);
}
return classes;
}
Any help is truly appreciated!
Edit 2
I also noticed another problem, is that when I detect a subclass I get the file name currentPath.getFileName() which might or might not be the subclass name as the subclass may be a nested or non-public class in the same file.
I strongly recommend parsing compiled class files instead of source code. Since these class files are already optimized for being processed by machines, a lot of the complexity and corner cases of the source code file processing has been eliminated.
So a solution to build a complete class hierarchy tree using the ASM library would look like this:
public static Map<String, Set<String>> getClassHierarchy(Path root) throws IOException {
return Files.walk(root)
.filter(p->Files.isRegularFile(p) && isClass(p.getFileName().toString()))
.map(p -> getClassAndSuper(p))
.collect(Collectors.groupingBy(Map.Entry::getValue,
Collectors.mapping(Map.Entry::getKey, Collectors.toSet())));
}
private static boolean isClass(String fName) {
// skip package-info and module-info
return fName.endsWith(".class") && !fName.endsWith("-info.class");
}
private static Map.Entry<String,String> getClassAndSuper(Path p) {
final class CV extends ClassVisitor {
Map.Entry<String,String> result;
public CV() {
super(Opcodes.ASM5);
}
#Override
public void visit(int version, int access,
String name, String signature, String superName, String[] interfaces) {
result = new AbstractMap.SimpleImmutableEntry<>(
Type.getObjectType(name).getClassName(),
superName!=null? Type.getObjectType(superName).getClassName(): "");
}
}
try {
final CV visitor = new CV();
new ClassReader(Files.readAllBytes(p)).accept(visitor, ClassReader.SKIP_CODE);
return visitor.result;
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
As a bonus, resp. to create some test cases, the following method adds the ability to build the hierarchy for a runtime class’ source:
public static Map<String, Set<String>> getClassHierarchy(Class<?> context)
throws IOException, URISyntaxException {
Path p;
URI clURI = context.getResource(context.getSimpleName()+".class").toURI();
if(clURI.getScheme().equals("jrt")) p = Paths.get(URI.create("jrt:/modules"));
else {
if(!clURI.getScheme().equals("file")) try {
FileSystems.getFileSystem(clURI);
} catch(FileSystemNotFoundException ex) {
FileSystems.newFileSystem(clURI, Collections.emptyMap());
}
String qn = context.getName();
p = Paths.get(clURI).getParent();
for(int ix = qn.indexOf('.'); ix>0; ix = qn.indexOf('.', ix+1)) p = p.getParent();
}
return getClassHierarchy(p);
}
Then, you can do
Map<String, Set<String>> hierarchy = getClassHierarchy(Number.class);
System.out.println("Direct subclasses of "+Number.class);
hierarchy.getOrDefault("java.lang.Number", Collections.emptySet())
.forEach(System.out::println);
and get
Direct subclasses of class java.lang.Number
java.lang.Float
java.math.BigDecimal
java.util.concurrent.atomic.AtomicLong
java.lang.Double
java.lang.Long
java.util.concurrent.atomic.AtomicInteger
java.lang.Short
java.math.BigInteger
java.lang.Byte
java.util.concurrent.atomic.Striped64
java.lang.Integer
or
Map<String, Set<String>> hierarchy = getClassHierarchy(Number.class);
System.out.println("All subclasses of "+Number.class);
printAllClasses(hierarchy, "java.lang.Number", " ");
private static void printAllClasses(
Map<String, Set<String>> hierarchy, String parent, String i) {
hierarchy.getOrDefault(parent, Collections.emptySet())
.forEach(x -> {
System.out.println(i+x);
printAllClasses(hierarchy, x, i+" ");
});
}
to get
All subclasses of class java.lang.Number
java.lang.Float
java.math.BigDecimal
java.util.concurrent.atomic.AtomicLong
java.lang.Double
java.lang.Long
java.util.concurrent.atomic.AtomicInteger
java.lang.Short
java.math.BigInteger
java.lang.Byte
java.util.concurrent.atomic.Striped64
java.util.concurrent.atomic.LongAdder
java.util.concurrent.atomic.LongAccumulator
java.util.concurrent.atomic.DoubleAdder
java.util.concurrent.atomic.DoubleAccumulator
java.lang.Integer
DISCLAIMER: This solution might not work if you have several classes with the same name as it does not take packages names into account.
I think you can do it with keeping track of the classes to lookup in a List and use a while loop until all the values on the list have been explored.
Here is a bit of code which creates a Map<String, List<String>>, key is the class name, value is the list of child classes.
public class Test {
private static Path appPath = //your path
private static Map<String, List<String>> classes = new HashMap<>();
private static List<String> classesToDo = new ArrayList<>();
public static void main(String[] args) throws IOException {
classesToDo.add("AnswerValueValidatorBase");
while(classesToDo.size() > 0) {
String className = classesToDo.remove(0);
List<String> subclasses = getDirectSubclasses(className);
if(subclasses.size() > 0)
classes.put(className, subclasses);
classesToDo.addAll(subclasses);
}
System.out.println(classes);
}
private static List<String> getDirectSubclasses(String className) throws IOException {
List<String> subclasses = new ArrayList<>();
Files.walk(appPath)
.filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".java"))
.forEach(path -> {
try {
List<String> lines = Files.readAllLines(path);
Pattern pattern = Pattern.compile("\\bextends "+className+"\\b");
Matcher matcher = pattern.matcher(lines.stream().collect(Collectors.joining(" ")));
boolean isChild = matcher.find();
if(isChild) {
String fileName = path.getFileName().toString();
String clazzName = fileName.substring(0, fileName.lastIndexOf("."));
subclasses.add(clazzName);
}
} catch(IOException e) {
//handle IOE
}
});
return subclasses;
}
}
Running it on my project returns something that looks correct
{
AnswerValueValidatorBase=[SingleNumericValidator, DefaultValidator, RatingValidator, ArrayValidatorBase, DocumentValidator],
ArrayValidatorBase=[MultiNumericValidator, StringArrayValidator, IntegerArrayValidator, MultiCheckboxValidator],
DefaultValidator=[IntegerValidator, DateValidator, StringValidator, CountryValidator, PercentageValidator],
IntegerArrayValidator=[MultiPercentageValidator, RankValidator, MultiDropValidator, MultiRadioValidator, CheckboxValidator],
SingleNumericValidator=[SliderValidator],
MultiNumericValidator=[MultiSliderValidator],
StringArrayValidator=[MultiTextValidator, ChecklistValidator]
}
EDIT
A recursive way of doing it would be
public class Test {
private static Path appPath = // your path
public static void main(String[] args) throws IOException {
List<String> classesToDo = new ArrayList<>();
classesToDo.add("AnswerValueValidatorBase");
Map<String, List<String>> classesMap = getSubclasses(new HashMap<>(), classesToDo);
System.out.println(classesMap);
}
private static Map<String, List<String>> getSubclasses(Map<String, List<String>> classesMap, List<String> classesToDo) throws IOException {
if(classesToDo.size() == 0) {
return classesMap;
} else {
String className = classesToDo.remove(0);
List<String> subclasses = getDirectSubclasses(className);
if(subclasses.size() > 0)
classesMap.put(className, subclasses);
classesToDo.addAll(subclasses);
return getSubclasses(classesMap, classesToDo);
}
}
private static List<String> getDirectSubclasses(String className) throws IOException {
// same as above
}
}
Calling java.lang.reflect.Type.toString() provides very nice representation of generic types:
#Test
public void typeToStringWithTypeToken() {
assertEquals("java.util.List<java.lang.String>", new TypeToken<List<String>>() {}.getType().toString());
}
What I need is the reverse of Type.toString() method, i.e. a method that can create Types from given string representation:
public static Type parseTypeString(String typeString) throws ClassNotFoundException {
if (typeString.indexOf('<') == -1) {
return Class.forName(typeString);
}
// ??? how to implement rest
throw new IllegalStateException("not implemented");
}
Which can pass following tests with specialized types:
#Test
public void justSimpleClassName() throws Exception {
assertEquals(Integer.class, parseTypeString("java.lang.Integer"));
}
#Test
public void listOfInteger() throws Exception {
assertEquals(new TypeToken<List<Integer>>() {}.getType(), parseTypeString("java.util.List<java.lang.Integer>"));
}
#Test
public void complexType() throws Exception {
assertEquals(new TypeToken<Map<List<Integer>, Set<String>>>() {}.getType(), parseTypeString("java.util.Map<java.util.List<java.lang.Integer>, java.util.Set<java.lang.String>>"));
}
I couldn't find a library or a SO question that addresses this problem.
Well... You can do it by yourself using for example antlr4 using following grammar
type returns[ClassBuilder value]
: cls=CLASS { $value = ClassBuilder.parse($cls.text); }
| cls=CLASS { $value = ClassBuilder.parse($cls.text); }
LT head=type { $value.add($head.value); }
(COMMA tail=type { $value.add($tail.value); })* GT
;
GT : '>'
;
LT : '<'
;
COMMA
: ','
;
CLASS
: ('a'..'z'|'A'..'Z') ('a'..'z'|'A'..'Z'|'0'..'9'|'$'|'.'|'_')*
;
Where ClassBuilder looks like
public class ClassBuilder {
private final Class<?> clazz;
private final List<ClassBuilder> parameters = new ArrayList<>();
public ClassBuilder(final Class<?> clazz) {
this.clazz = clazz;
}
public static ClassBuilder parse(String clazz) {
try {
return new ClassBuilder(Class.forName(clazz));
} catch (ClassNotFoundException e) {
// do better handling here
throw new IllegalStateException(e);
}
}
public void add(ClassBuilder builder) {
parameters.add(builder);
}
public Type build() {
// class is not parametrized
if (parameters.isEmpty()) {
return clazz;
}
return ParameterizedTypeImpl.make(
clazz,
parameters.stream()
.map(ClassBuilder::build)
.toArray(Type[]::new),
null);
}
}
And then finally parse string
CharStream stream =
new ANTLRInputStream(
"java.util.Map<java.util.List<java.lang.Integer>, java.util.Set<java.lang.String>>"
);
TokenStream tokenStream =
new CommonTokenStream(new ParametrizedTypeLexer(stream));
ParametrizedTypeParser parser =
new ParametrizedTypeParser(tokenStream);
assertEquals(
new TypeToken<Map<List<Integer>, Set<String>>>() {}.getType(),
parser.type().value.build()
);
You can see working example here.
NOTE
CLASS lexer rule may be a bit inaccurate. Also this parser only applies to non-primitive classes and parametrized types, but you of course extend it to support wildcard types as well.
The output of java.lang.reflect.Type.toString() is implementation specific.
The behaviour you are seeing is part of Gson's ParameterizedTypeImpl
Due to type erasure, classes in Java don’t have type parameters at runtime.
The Type from gson doesn’t represent an actual loaded Class.
See Get Type of ParameterizedType from generic for related information.
I could workaround this problem but I cannot understand it, so I am asking for some explanation (and maybe a better question title as well).
Please consider this:
public class TBGService {
// TBGObject is an abstract base class which is extended by model classes
public <T> T doGet(TBGObject model) throws TBGServiceException {
String uri = model.buildUrl(repository) + model.getObjectKey();
GetMethod method = new GetMethod(uri);
T returned = execute(method, credentials, model.getClass());
return returned;
}
}
and this:
public enum TBGTaskAttributes {
private TBGTaskAttributes(String id, String type, String label, Object... flags) {
builder = new TaskAttributeBuilder();
builder.withId(id).withLabel(label);
for (Object flag : flags) {
processFlag(flag);
}
}
public abstract String getValueFromIssue(TBGIssue issue);
public abstract void setValueInIssue(TBGIssue issue, String value);
}
when I write this code to define an enum item:
PROJECT(TaskAttribute.PRODUCT, TaskAttribute.TYPE_SINGLE_SELECT, "Project", new OptionProvider() {
#Override
public Set<Entry<String, String>> getOptions(TaskRepository repository) {
try {
List<TBGProject> list = TBGService.get(repository)
.doGet(new TBGProjects()).getProjects();
[...]
return map.entrySet();
} catch (TBGServiceException e) { [...] }
return null;
}
}) {
#Override
public String getValueFromIssue(TBGIssue issue) {
return issue.getProjectKey();
}
#Override
public void setValueInIssue(TBGIssue issue, String value) {
issue.setProjectKey(value);
}
},
[... other items ...]
I get compiler error (also eclipse auto-completion does not work):
The method getProjects() is undefined for the type Object
and if I hover the doGet method, eclipse show it as defined like:
<Object> Object TBGService.doGet(TBGObject model)
Elsewhere, hovering shows the signature correctly as:
<TBGProjects> TBGProjects TBGService.doGet(TBGObject model)
when called with parameter new TBGProjects().
Just changing:
List<TBGProject> list = TBGService.get(repository)
.doGet(new TBGProjects()).getProjects();
with:
TBGProjects projects = TBGService.get(repository).doGet(new TBGProjects());
List<TBGProject> = projects.getProjects();
makes it work. But what's happening here? What am I missing?
Java infers the type of T based on what you assign the return value of the method to.
If you don't assign the return value to anything, Java has no idea what T should be.
To fix this, you can change the parameter to be of type T so Java can infer T from the parameter you pass:
public <T extends TBGObject> T doGet(T model) throws TBGServiceException {
I'm learning about Java enums and I was wondering what is the best approach to check multiple enums for a matching value in order to call a specific method. I have defined two separate enums below that are used by getValue method's colName parameter to determine what method to execute. So the enum drives the method call. There has to be a more efficient way to do this than what I have below. Any suggestions?
I want to avoid having to do the below (pseudo code):
if(colName.equalsIgnoreCase("ATTRIBUTEONE") ||
colName.equalsIgnoreCase("ATTRIBUTETWO") ||
colName.equalsIgnoreCase("ATTRIBUTETWO")){
callAsStringMethod();
} else if(colName.equalsIgnoreCase("ATTRIBUTEFOUR")){
callAsIntegerMethod();
}
My Attempt using enum:
public class RowHelper implements IRowHelper
public static enum StringAttributes {
ATTRIBUTEONE,
ATTRIBUTETWO,
ATTRIBUTETHREE;
}
public static enum IntegerAttributes {
ATTRIBUTEFOUR,
ATTRIBUTEFIVE,
ATTRIBUTESIX,
ATTRIBUTESEVEN;
}
#Override
public String getValue(String colName) throws Exception{
boolean colFound=false;
Object retValue = null;
for (EConstants.StringAttributes attribute : EConstants.StringAttributes.values()) {
if(colName.toUpperCase().equals(attribute)){
retValue = callAsStringMethod();
colFound=true;
}
}
for (EConstants.IntegerAttributes attribute : EConstants.IntegerAttributes.values()) {
if(colName.toUpperCase().equals(attribute)){
retValue = callAsIntegerMethod();
colFound=true;
}
}
if(!colFound)
throw new Exception("column not found");
if(retValue instanceof String )
return (String) retValue;
else
return retValue.toString();
}
}
Try this:
public String getValue(String colName) throws Exception {
final String name = colName != null ? colName.trim().toUpperCase() : "";
try {
EConstants.StringAttributes.valueOf(name);
return callAsStringMethod().toString();
} catch (Exception e1) {
try {
EConstants.IntegerAttributes.valueOf(name);
return callAsIntegerMethod().toString();
} catch (Exception e2) {
throw new Exception("column not found");
}
}
}
The method's now returning the appropriate value, according to the latest edit of the question.
EDIT :
According to Kirk Woll and Louis Wasserman's benchmark, looping through values is significantly faster than doing a try/catch. So here's a simplified version of the original code, expect it to be a bit faster:
public String getValue(String colName) throws Exception {
final String name = colName != null ? colName.trim().toUpperCase() : "";
for (EConstants.StringAttributes attribute : EConstants.StringAttributes.values())
if (name.equals(attribute))
return callAsStringMethod().toString();
for (EConstants.IntegerAttributes attribute : EConstants.IntegerAttributes.values())
if (name.equals(attribute))
return callAsIntegerMethod().toString();
throw new Exception("column not found");
}
Well, this is a weird design ._. Anyway, you can use enum, but I would something like:
public interface RowAttribute {
String getValue(IRowHelper rowHelper);
}
public class StringRowAttribute implements RowAttribute {
#Override
public String getValue(IRowHelper rowHelper) {
return rowHelper.callAsStringMethod();
}
}
public class IntegerRowAttribute implements RowAttribute {
#Override
public String getValue(IRowHelper rowHelper) {
return rowHelper.callAsIntegerMethod().toString();
}
}
public class RowHelper implements IRowHelper {
private static final RowAttribute INTEGER_ATTRIBUTE = new IntegerRowAttribute();
private static final RowAttribute STRING_ATTRIBUTE = new StringRowAttribute();
private static enum Attribute {
ATTRIBUTEONE(INTEGER_ATTRIBUTE),
ATTRIBUTETWO(INTEGER_ATTRIBUTE),
ATTRIBUTETHREE(INTEGER_ATTRIBUTE);
ATTRIBUTEFOUR(STRING_ATTRIBUTE),
ATTRIBUTEFIVE(STRING_ATTRIBUTE),
ATTRIBUTESIX(STRING_ATTRIBUTE),
ATTRIBUTESEVEN(STRING_ATTRIBUTE);
private final RowAttribute attribute;
private Attribute(RowAttribute attribute) {
this.attribute = attribute;
}
public RowAttribute getAttributeResolver() {
return this.attribute;
}
}
#Override
public String getValue(String colName) throws Exception {
final String name = colName != null ? colName.trim() : "";
for (Attribute attribute : Attribute.values()) {
if (attribute.name().equalsIgnoreCase(name)) {
return attribute.getAttributeResolver().getValue(this);
}
}
throw new Exception(String.format("Attribute for column %s not found", colName));
}
}
Then you don't need to create more than one enum and use its power to iterate through the possible values. You would only need to make the methods callAsStringMethod/callAsIntegerMethod public. Another way is to insert the implementations inside RowHelper. Something like this:
public class RowHelper implements IRowHelper {
public interface RowAttribute {
String getValue();
}
private static final RowAttribute INTEGER_ATTRIBUTE = new RowAttribute() {
#Override
public String getValue() {
return callAsIntegerMethod().toString();
}
};
private static final RowAttribute STRING_ATTRIBUTE = new RowAttribute() {
#Override
public String getValue() {
return callAsStringMethod();
}
};
...
#Override
public String getValue(String colName) throws Exception {
...
if (attribute.name().equalsIgnoreCase(name)) {
return attribute.getAttributeResolver().getValue();
}
...
}
}
Anyway, I don't understand in your method how you get the attribute value really without passing as parameter the colName to it.
The most efficient way to do this with multiple enums is, frankly, to make them the same enum. There isn't really a better way.
That said, instead of the loop you have, you can use Enum.valueOf(EnumClass.class, name) to find the enum value of that type with the specified name, rather than looping like you're doing.
Is there any way to pass class as a parameter in Java and fire some methods from that class?
void main()
{
callClass(that.class)
}
void callClass(???? classObject)
{
classObject.somefunction
// or
new classObject()
//something like that ?
}
I am using Google Web Toolkit and it does not support reflection.
public void foo(Class c){
try {
Object ob = c.newInstance();
} catch (InstantiationException ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
}
}
Here are some good examples on Reflection API
How to invoke method using reflection
import java.lang.reflect.*;
public class method2 {
public int add(int a, int b)
{
return a + b;
}
public static void main(String args[])
{
try {
Class cls = Class.forName("method2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Method meth = cls.getMethod(
"add", partypes);
method2 methobj = new method2();
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj
= meth.invoke(methobj, arglist);
Integer retval = (Integer)retobj;
System.out.println(retval.intValue());
}
catch (Throwable e) {
System.err.println(e);
}
}
}
Also See
Java Reflection
public void callingMethod(Class neededClass) {
//Cast the class to the class you need
//and call your method in the class
((ClassBeingCalled)neededClass).methodOfClass();
}
To call the method, you call it this way:
callingMethod(ClassBeingCalled.class);
Construct your method to accept it-
public <T> void printClassNameAndCreateList(Class<T> className){
//example access 1
System.out.print(className.getName());
//example access 2
ArrayList<T> list = new ArrayList<T>();
//note that if you create a list this way, you will have to cast input
list.add((T)nameOfObject);
}
Call the method-
printClassNameAndCreateList(SomeClass.class);
You can also restrict the type of class, for example, this is one of the methods from a library I made-
protected Class postExceptionActivityIn;
protected <T extends PostExceptionActivity> void setPostExceptionActivityIn(Class <T> postExceptionActivityIn) {
this.postExceptionActivityIn = postExceptionActivityIn;
}
For more information, search Reflection and Generics.
Use
void callClass(Class classObject)
{
//do something with class
}
A Class is also a Java object, so you can refer to it by using its type.
Read more about it from official documentation.
This kind of thing is not easy. Here is a method that calls a static method:
public static Object callStaticMethod(
// class that contains the static method
final Class<?> clazz,
// method name
final String methodName,
// optional method parameters
final Object... parameters) throws Exception{
for(final Method method : clazz.getMethods()){
if(method.getName().equals(methodName)){
final Class<?>[] paramTypes = method.getParameterTypes();
if(parameters.length != paramTypes.length){
continue;
}
boolean compatible = true;
for(int i = 0; i < paramTypes.length; i++){
final Class<?> paramType = paramTypes[i];
final Object param = parameters[i];
if(param != null && !paramType.isInstance(param)){
compatible = false;
break;
}
}
if(compatible){
return method.invoke(/* static invocation */null,
parameters);
}
}
}
throw new NoSuchMethodException(methodName);
}
Update:
Wait, I just saw the gwt tag on the question. You can't use reflection in GWT
Adding <T> T as return type worked for me. Ex with json deserialize
public static <T> T fromJson(String json, Class<T> classOfT){
return gson().fromJson(json, classOfT);
}
I am not sure what you are trying to accomplish, but you may want to consider that passing a class may not be what you really need to be doing. In many cases, dealing with Class like this is easily encapsulated within a factory pattern of some type and the use of that is done through an interface. here's one of dozens of articles on that pattern: http://today.java.net/pub/a/today/2005/03/09/factory.html
using a class within a factory can be accomplished in a variety of ways, most notably by having a config file that contains the name of the class that implements the required interface. Then the factory can find that class from within the class path and construct it as an object of the specified interface.
As you said GWT does not support reflection. You should use deferred binding instead of reflection, or third party library such as gwt-ent for reflection suppport at gwt layer.
Se these:
http://download.oracle.com/javase/tutorial/extra/generics/methods.html
here is the explaniation for the template methods.
Have a look at the reflection tutorial and reflection API of Java:
https://community.oracle.com/docs/DOC-983192enter link description here
and
http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html
Class as paramater. Example.
Three classes:
class TestCar {
private int UnlockCode = 111;
protected boolean hasAirCondition = true;
String brand = "Ford";
public String licensePlate = "Arizona 111";
}
--
class Terminal {
public void hackCar(TestCar car) {
System.out.println(car.hasAirCondition);
System.out.println(car.licensePlate);
System.out.println(car.brand);
}
}
--
class Story {
public static void main(String args[]) {
TestCar testCar = new TestCar();
Terminal terminal = new Terminal();
terminal.hackCar(testCar);
}
}
In class Terminal method hackCar() take class TestCar as parameter.