A couple of days ago I posted my code that models systems made up of moving parts. I got several great tips and ideas and here is my latest version. My actions are now methods annotated with @Action (instead of separate classes as in my original code). My Model
class contains a HashMap
of Class, ArrayList<? extends Agent>
so each ArrayList
contains agents of the same type.
In myModel.start()
method I get annotated methods for each class and invoke them on each ArrayList
member. The code looks more promising than the original one, but my main concern is that I had to use reflection to invoke actions on each model agent as I read that reflection is costly.
I would also like to hear your opinions on my usage of generics as I am new to the concept.
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class GenericsTest { public static void main(String[] args) throws NoSuchFieldException, SecurityException { Model machine = new Model(); ArrayList<Gear> gears = new ArrayList<Gear>(); ArrayList<Lever> levers = new ArrayList<Lever>(); int i; for (i = 0; i < 1000; i++) { Gear gear = new Gear(); gear.setDiameter(i); gears.add(gear); Lever lever = new Lever(); lever.setLength(i + 2000); levers.add(lever); } machine.addAgentList(Gear.class, gears); machine.addAgentList(Lever.class, levers); machine.start(); } } class Model { public void start() { for (Class<? extends Agent> clazz : listOfAgents.keySet()) { List<Method> actions = new ArrayList<Method>(); for (Method m : clazz.getDeclaredMethods()) { if (m.isAnnotationPresent(Action.class)) { actions.add(m); } } for (Agent agent : listOfAgents.get(clazz)) { for (Method action : actions) { try { action.invoke(agent, new Object[] {}); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); } } } } } public Map<Class<? extends Agent>, ArrayList<? extends Agent>> getAgentLists() { return listOfAgents; } public void addAgentList(Class<? extends Agent> cls, ArrayList<? extends Agent> agents) { listOfAgents.put(cls, agents); } private final Map<Class<? extends Agent>, ArrayList<? extends Agent>> listOfAgents = new HashMap<Class<? extends Agent>, ArrayList<? extends Agent>>(); } interface Agent { } @Retention(RetentionPolicy.RUNTIME) @Target(value = ElementType.METHOD) @interface Action { } /* * Agent classes */ class Gear implements Agent { private int diameter; public int getDiameter() { return diameter; } public void setDiameter(int diameter) { this.diameter = diameter; } @Action public void rotate() { System.out.println("rotating ... " + this.toString() + " " + this.diameter); } @Action public void replace() { System.out.println("replacing ..." + this.toString() + " " + this.diameter); } } class Lever implements Agent { private int length; public int getLength() { return length; } public void setLength(int length) { this.length = length; } @Action public void move() { System.out.println("moving ..." + this.toString() + " " + this.length); } }
I will also try improve my original FactoryClass
idea making Action
an interface and post the code in another post.
EDIT
Changed the code to improve my list declarations:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class GenericsTest { public static void main(String[] args) throws NoSuchFieldException, SecurityException { Model machine = new Model(); List<Gear> gears = new ArrayList<Gear>(); List<Lever> levers = new ArrayList<Lever>(); int i; for (i = 0; i < 1000; i++) { Gear gear = new Gear(); gear.setDiameter(i); gears.add(gear); Lever lever = new Lever(); lever.setLength(i + 2000); levers.add(lever); } machine.addAgentList(Gear.class, gears); machine.addAgentList(Lever.class, levers); machine.start(); } } class Model { public void start() { for (Class<? extends Agent> clazz : listOfAgents.keySet()) { List<Method> actions = new ArrayList<Method>(); for (Method m : clazz.getDeclaredMethods()) { if (m.isAnnotationPresent(Action.class)) { actions.add(m); } } for (Agent agent : listOfAgents.get(clazz)) { for (Method action : actions) { try { action.invoke(agent, new Object[] {}); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); } } } } } public Map<Class<? extends Agent>, List<? extends Agent>> getAgentLists() { return listOfAgents; } public void addAgentList(Class<? extends Agent> cls, List<? extends Agent> agents) { listOfAgents.put(cls, agents); } private final Map<Class<? extends Agent>, List<? extends Agent>> listOfAgents = new HashMap<Class<? extends Agent>, List<? extends Agent>>(); } interface Agent { } @Retention(RetentionPolicy.RUNTIME) @Target(value = ElementType.METHOD) @interface Action { } /* * Agent classes */ class Gear implements Agent { private int diameter; public int getDiameter() { return diameter; } public void setDiameter(int diameter) { this.diameter = diameter; } @Action public void rotate() { System.out.println("rotating ... " + this.toString() + " " + this.diameter); } @Action public void replace() { System.out.println("replacing ..." + this.toString() + " " + this.diameter); } } class Lever implements Agent { private int length; public int getLength() { return length; } public void setLength(int length) { this.length = length; } @Action public void move() { System.out.println("moving ..." + this.toString() + " " + this.length); } }
ArrayList<...>
reference types should be simplyList<...>
. But you still haveArrayList
s all over.\$\endgroup\$