Reputation: 4698
I have a ListView and every time the selection is changed, I want to call a class with that name. For example, if the item is called "Text String" then the class TextString should be called. The code that I currently have is giving me an error saying The method insert(ArrayList<Element>) is undefined for the type Object
... Eclipse gives me a suggestion to cast the object as Element, but that doesn't do anything. The Element class is a superclass and TextString would implement that class.
Here is the code I have so far:
elementList.itemsProperty().bind(listProperty);
listProperty.set(FXCollections.observableArrayList(elementListItems));
elementList.setOnMouseClicked(new EventHandler<MouseEvent>() {
public String selectedElement = "Text String";
@Override
public void handle(MouseEvent event) {
selectedElement = (String)elementList.getSelectionModel().getSelectedItem();
selectedElement = selectedElement.replace(" ", "");
Class<?> clazz;
try {
clazz = Class.forName("elements."+selectedElement);
Constructor<?> ctor = clazz.getConstructor();
Object object = ctor.newInstance();
Method meth = clazz.getClass().getMethod("insert", new Class<?>[] { Canvas.class, ArrayList.class, GraphicsContext.class });
meth.invoke(object, canvas, objects, gc);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
});
Element.java
public abstract class Element {
public String name;
public String description;
public Canvas canvas;
public ArrayList<Element> objects;
public GraphicsContext gc;
void remove(){
}
void toggle(){
}
void setBounds(int x, int y, int w, int h){
}
public abstract void insert(Canvas canvas, ArrayList<Element> objects, GraphicsContext gc);
}
TextString.java
public class TextString extends Element {
private GraphicsContext gc;
TextString() {
super();
this.name = "Text String";
this.description = "A literal readable string of text.";
}
@Override
public void insert(Canvas canvas, ArrayList<Element> objects, GraphicsContext gc) {
this.gc = gc;
this.canvas = canvas;
this.objects = objects;
System.out.println("Text string created.");
}
}
How can I cast the object to whatever object is being selected by the listview?
Upvotes: 5
Views: 1046
Reputation: 3372
What you need to do is simply have an interface MyElement with method execute Implementation of which will call whatever you need inside the "execute()"
basically I suggest command pattern, which is basis for all UI operations when doing BE
my 2 cents, implemntation example from my old-old project
CommandFactory factory = CommandFactory.getInstance();
Command command = factory.createCommand(relativeURL);
if(command != null){
command.execute( /* Object */ input);
}
}
into factory
/**
* Factory for commands
*
* @author andre
*/
public class CommandFactory {
// singleton instance
protected static CommandFactory instance = new CommandFactory();
//cache of objects
Map<String, Command> cache = new HashMap<>();
// protected constructor prevents creating object outside
protected CommandFactory(){
}
// return signleton instance of factory
public static CommandFactory getInstance(){
return instance;
}
/**
* create and return appropriate command
*
* @return found command
* @throws exception if something bad happened
*/
public Command createCommand(String classname){
try {
Command com = cache.get(URL);
if(com == null){
Command temp = (Command) Class.forName(classname).newInstance();
if(temp != null){
cache.put(URL, temp);
com = cache.get(URL);
}
}
return com;
} catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
Logger.getLogger(getClass().getName()).error(ex);
return new Error404();
}
}
}
into interface
public interface Command{
void execute(Object input);
}
and implementation example
public class EditUserInfo implements Command {
@Override
public void execute(Object object) {
callWhatever((String) object);
}
private void callWhatever(String textFromField){
// inner logic example
}
}
Upvotes: 0
Reputation: 5871
There is another way you can handle this, use a HashMap
to store all the implementations as follows (this can be done when your application is started, the performance will be better).
Map<String, Element> objects = new HashMap<>();
objects.put("Text String", new TextString());
And the handle method becomes..
@Override
public void handle(MouseEvent event) {
selectedElement = (String) elementList.getSelectionModel().getSelectedItem();
// selectedElement = selectedElement.replace(" ", "");
objects.get(selectedElement).insert(Canvas.class, ArrayList.class, GraphicsContext.class);
}
This will be more readable and clean since we are not using Reflections.
Upvotes: 0
Reputation: 9978
I believe you are over complicating everything by trying to use reflection. It is optimal but sometimes I usually just rather do things simpler.
Why don't you make a factory helper that would import all the classes you might need and then use a switch to return the instance that you need?
Like:
public MyInterface returnClass(String type){
switch (type){
case: "Text String":
return new TextString():
//And so on
}
}
Upvotes: 0
Reputation: 1801
clazz.getMethod(...)
instead of clazz.getClass().getMethod(...)
objects
to be ArrayList
instance, not List
.Your code should like next:
try {
clazz = Class.forName("elements."+selectedElement);
Constructor<?> ctor = clazz.getConstructor();
Element object = (Element) ctor.newInstance();
Method meth = clazz.getMethod("insert", new Class<?>[] { Canvas.class, ArrayList.class, GraphicsContext.class });
meth.invoke(object, canvas, objects, gc);
} catch (ClassNotFoundException e) {
Tip. I'd suggest changing the type of ArrayList parameter in insert method to List. And also to catch only one generic Exception, instead of bunch of specific one.
Let me know if you any further issues with that.
Upvotes: 4