开发者

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 :

  1. Find if text entered by user corresponds to an existing class (this can be done using the reflections framework, I guess)
  2. 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜