Taking a String and making an object from a class that String represents in Java
Scenario:
- I have two classes named ListLayout and GridLayout which both implement CustomLayout.
- The user enters a String representing the layout they wish to use ("ListLayout" for example)
How can I create a ListLayout object based on the string entered by the user? I would need to be equivalent to just doing this:
CustomLayout layout = new ListLayout();
Ideally I would need to find a solution which would allow me to check if the S开发者_开发技巧tring entered corresponds to a predefined class which implements CustomLayout before actually making the object (because it will throw an error if it doesn't exist and I don't check beforehand).
This is really getting me thinking.... thanks in advance for your help
If you don't want to use reflection here, a map of factories could be the right thing:
interface LayoutFactory {
public CustomLayout makeLayout();
}
Map<String, LayoutFactory> factories = new HashMap<String, LayoutFactory>();
factories.put("GridLayout", new LayoutFactory() {
public CustomLayout makeLayout() { return new GridLayout(); }
});
factories.put("ListLayout", new LayoutFactory() {
public CustomLayout makeLayout() { return new ListLayout(); }
});
String layoutName = ...; // user input
CustomLayout l = factories.get(layoutName).makeLayout();
Of course, you also should handle the case where the user did give an unknown layout name (factories.get then returns null
).
Do something like this:
String userInput = //get user's input
while(!userInput.equalsIgnoreCase("ListLayout") && !userInput.equalsIgnoreCase("gridLayout")){
System.out.println("Please enter a valid option");
userInput = //get user's input again
}
CustomLayout layout;
if(userInput.equalsIgnoreCase("ListLayout")
layout = new ListLayout();
else
layout = new GridLayout();
I would personally not recommend using reflection; as refactoring becomes a pain.
Better would be to do a string check and instantiate the correct class.
For example
if(userInput.equals("ListLayout")) {
CustomLayout layout = new ListLayout();
} else if (userInput.equals("GridLayout")) {
CustomLayout layout = new GridLayout();
}
This can also be implemented using java reflection as others pointed out. But if you want to refactor the code later on (say using eclipse refactoring for example), then the reflection code will not be auto-refactored. For example, if you used reflection and if you changed the class name of ListLayout to FancyListLayout, and do eclipse auto refactoring, the reflection code will be left untouched and your code will break.
This is the code used to obtain an instance of a class if you have a String with the fully qualified name:
try {
CustomLayout layout = (CustomLayout) Class.forName("your.package.ListLayout").newInstance();
} catch (Exception ex) {
}
The exception can be of type: LinkageError
, ExceptionInInitializerError
, ClassNotFoundException
, IllegalAccessException
, InstantiationException
and SecurityException
and it is recommended to have catch clauses for each of them if you want to handle them differently.
Two steps :
- Find if text entered by user corresponds to an existing class (this can be done using the reflections framework, I guess)
- Instanciate an object of that class, which is usually done using
Class.forName(String)
Ok. What you should be looking into is called Reflection (wiki on reflection) and java offers a rich API for that. THis basically allows you to generate objects from a String and catch the execption if there is no such class accordingly. This has however some drawbacks, please check on the API for further reference.
Cheers!
Reflection is the natural answer, but you could create a Factory for it.
public class CustomLayoutFactory {
public CustomLayout createInstance(String layoutName) {
if("ListLayout".equals(layoutName) {
return new ListLayout();
} else if("GridLayout".equals(layoutName) {
return new GridLayout();
}
return null;
}
}
While not the most elegant solution, it is useful in cases where the SecurityManager
is too restrictive for reflection.
Reading all the answers I'd like to strongly recommend against the reflection based ones, since you will have to be very careful in renaming your classes afterwards.
Also instead of:
if ("ListLayout".equals(userInput)) {
return new ListLayout();
}
you can add a protected field inside your base Layout
class:
public abstract class Layout {
protected String userInputName;
}
and modifying its extenders like so:
public class ListLayout {
public ListLayout() {
userInputName = "listLayout"; // set protected field
}
}
Then you can do:
for (Layout l : setOfAllLayouts) {
if (userInput.equals(l.getInputName)) {
return l.clone();
}
}
This ought to do it:
Class.forName(userInput).newInstance();
You can then use instanceof
to test for what class you ended up with. However, I'd test the value of userInput
before doing this, to make sure it's a recognized class name. You might also need to prepend the package name if your users just input the simple class name.
精彩评论