I’m using a modified version of JosAH’s text-based menu system and am trying to figure out how to use reflection to create a list of items from a class’ declared subclasses. Because different menu items use different parameters in order to manipulate the underlying data, I’m having a problem generalizing their instantiation.
For example, the assignment was to create a custom linked list, so the menu options would consist of Print, Insert, Save, Quit etc. Printing and insertion just require a reference to the linked list in order to parse the input and call the appropriate functions in the linked list. Save additionally requires the file to save to while Quit doesn’t require any additional arguments.
public class MenuList extends TextMenu { //TextMenu extends MenuItem
List<MenuItem> items;
public MenuList (Object data, File file) {} //calls fillList
//calls createList which fills a List<MenuItem> and copies it to this.items
protected void fillList(Object data, File file) {
this.items.addAll( createList(this.getClass(), ... args?) );
}
private static class MenuQuit extends MenuItem {
public MenuQuit() {} //does not need data
}
private static class MenuOne extends MenuItem {
public MenuOne(Object data) {} //manipulates data
}
private static class MenuTwo extends MenuItem {
public MenuOne(Object data) {} //also manipulates data
}
private static class MenuThree extends MenuItem {
//manipulates data and saves to file
public MenuThree(Object data, File file) {}
}
private static class NestedMenu extends MenuList {
//needs parameters to pass to it's own menuItems
public NestedMenu(Object data, File file) {}
}
}
public static List<MenuItem> createList(Class cls, ...args? ) {
List<MenuItem> items = new ArrayList<MenuItem>();
try {
Classes<?>[] param = { parse(args)? };
Class<?>[] menuList = cls.getDeclaredClasses();
for(Class<?> c : menuList) {
if(MenuItem.class.isAssignableFrom(c)) {//only adding the MenuItems
Constructor<?> ct = c.getConstructor(param);
Object menuItem = ct.newInstance( ...args? );
items.add( (MenuItem) menuItem );
}
}
} catch (Exception e) { }
}
Is there any way to generalize createList so that the menu items are created with the necessary parameters?
Also, because of how long I’ve been working on this, I’m completely open to the possibility that this is a completely overengineered solution and that I should scrap large parts, or perhaps the entirety, of this idea.
Edit: I don’t know if this is best place to add this piece information-
This was a solution I had been thinking about, and after reading through some responses and comments, it might be the correct one. If MenuItem always accepts a single Object as a parameter, I can make that assumption when calling getConstructor and newInstance. Afterwards, I’ll just leave it up the specific class to cast Object into something useful that they can pull their on data from.
public class MenuItem {
Object data;
public MenuItem(Object data) {
this.data = data;
parseData();
}
protected abstract void parseData();
}
private static class MenuQuit extends MenuItem {
public MenuQuit(Object data) { super(data) } //nothing additional needed
protected void parseData() {} //does nothing
}
private static class MenuSave extends MenuItem { //as outlined above
private CustomLinkedList list;
private File saveFile;
public MenuSave(Object data) {
super(data);
}
//may be able to use reflection to generalize this
protected void parseData() {
this.list = ((MyCustomData) data).list;
this.saveFile = ((MyCustomData) data).saveFile;
}
}
public class MyCustomData {
public CustomLinkedList list;
public File saveFile;
public int otherData;
public MyCustomData(CustomLinkedList a, File b, int c) {} //assignment
}
I was trying to avoid this method because it starts adding complexity to the subclassed MenuItems, which I was hoping to keep as simple as possible. Is there a way to improve this solution?
Not exactly an answer,
but since I’m generally not a big fan of reflection here is another idea:
You could add
dataandfileto your base classMenuItemand leave themnullwhere they are not applicable.