1

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 String 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

8
  • 2
    Believe me, this is NOT an instance where you want to use java reflection. As long as you know what classes they can choose from, don't over complicate it. Commented Jun 7, 2011 at 15:52
  • 1
    @MaxMackie As others agree, you should NOT use reflection. You should accept a different answer. Reflection is a poor choice of code in your scenario. Commented Jun 7, 2011 at 16:13
  • 1
    If the list of Layout classes the user can entered is from a list you control, don't get them to type it, have them select an option from a drop down. Then your code can simply switch or use if statements to instantiate the appropriate class. This isn't as flexible but YAGNI for now. Commented Jun 7, 2011 at 16:23
  • @Gweebz YAGNI is evil :) You don't EVER know if you're going to need something. Its generally better to use the most flexible approach there is, because if you don't you might (might = will most likely) regret it, since if your application is very successful and you need to maintain it you never know what your clients will require. Commented Jun 7, 2011 at 16:38
  • 1
    download.oracle.com/javase/tutorial/reflect/index.html "Reflection is powerful, but should not be used indiscriminately. If it is possible to perform an operation without using reflection, then it is preferable to avoid using it." Commented Jun 8, 2011 at 21:02

9 Answers 9

7

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).

Sign up to request clarification or add additional context in comments.

Comments

2

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();

5 Comments

That's the current solution I have, however I want to use less lines and be able to add new layouts without modifying this method.
If you are creating many subclasses, use a switch statement. That's what switch statements were made for. This is NOT what reflection was made for. Only use reflection if you have no idea what the class is that you are going to be using. In this case, you are in control of what classes can be implemented; use that to your advantage.
switch on Strings is only available from Java 7 on, I think.
"Unlike if-then and if-then-else statements, the switch statement can have a number of possible execution paths. A switch works with the byte, short, char, and int primitive data types. It also works with enumerated types (discussed in Enum Types), the String class, and a few special classes that wrap certain primitive types: Character, Byte, Short, and Integer (discussed in Numbers and Strings)." But yeah, I think you're right, that might have been implemented in java 7+
The list is apparently a fixed list of classes so an Enum would be appropriate and you can switch on those.
2

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.

Comments

2

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.

15 Comments

This is elegant and both creates the object and makes sure that the layout already exists. Thank you.
If you use reflection, then refactoring becomes a pain. For example eclipse auto-refactoring will completely ignore classes instantiated through reflection
This does answer the question in the manner requested, but it is most certainly NOT an elegant solution to a problem (no offense MaxMackie). Do you understand what you are forcing your program to do? Less lines of code != better. But w/e, it's your program. Ugh, going through the java meta data to find an already known class and then casting it without checks or anything... gl.
@MaxMackie rationalSpring is VERY right using reflection is never recommended if you can handle something without it, see my answer for details
you should not ever catch (Exception e), as this also catches the runtime exceptions, especially with reflection
|
1

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)

4 Comments

That solution is overly complex for the problem. No need to enter the deep dark forest of reflection unless absolutely necessary.
@MirroredFate reflection is not a deep dark forest and using reflection, this solution can be made future ready (unlike your solution)
If you use reflection, then refactoring becomes a pain. For example eclipse auto-refactoring will completely ignore classes instantiated through reflection.
@Teja Kantamneni Reflection IS a deep dark forest. It is EXTREMELY useful is very very select situations. Future ready? You mean like for the cases where user input is wrong, and your program will exception out, or do you mean it is a good solution because it would use such an extreme amount of resources and go through a number of checks that should even be in such a simple program? Yeah, that's GOOD programming. Designing around meta. Smart.
1

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!

Comments

1

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.

Comments

1

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();
    }
}

1 Comment

One more thing, PaĹ­lo Ebermann's answer is the right way to do it IMHO, this is a not-bad solution, but factories is the correct one.
0

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.

3 Comments

Thanks for that snippet, it's exactly what I need. Is there a way for me to check if the userInput is valid without going through all the possible classes it could be?
One way is to keep a hashset of recognized class names and test the user input against that. You can also just catch the appropriate exceptions.
If you use reflection, then refactoring becomes a pain. For example eclipse auto-refactoring will completely ignore classes instantiated through reflection