For something I'm doing, it would just make things so much easier, quicker, and cleaner to have C# style events. I've written a class I believe should replicate a C# style event using reflection. Since I've never actually used reflection before in any meaningful sense, I'm very much not happy using it without, at the very least, a second opinion.
I want to declare an event with:
public Event SomeEvent = new Event();
Raise the event (presumably within the declaring class) with (where SomeArguments is whatever class that may be specified in the Javadocs boxed in an Object):
SomeEvent.Raise(this, SomeArguments);
Add a method to be called when the event is raised with:
SomeEvent.AddListener(this, "onSomeEvent");
Stop a method being called when an event is raised with:
SomeEvent.RemoveListener(this, "onSomeEvent");
And cancel all listeners with:
SomeEvent.ClearListeners();
Methods to listen to the event should be formatted like:
public void onSomeEvent(object Sender, object SomeArguments)
{ }
And this is my class:
package puppy.hanii.library;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class Event
{
public Event()
{
Methods = new ArrayList<Method>();
DeclaringClasses = new ArrayList<Object>();
}
ArrayList<Method> Methods;
ArrayList<Object> DeclaringClasses;
public boolean AddListener(Object Class, String MethodName)
{
try
{
Method MTR = Class.getClass().getMethod(MethodName, Object.class, Object.class); // MTR = Method To Register.
if(Methods.contains(MTR))
return false;
else
{
Methods.add(MTR);
DeclaringClasses.add(Class);
return true;
}
}
catch (NoSuchMethodException error) { throw new RuntimeException("No such method, or correct overloads of method, exists."); }
}
public boolean RemoveListener(Object Class, String MethodName)
{
try
{
DeclaringClasses.remove(Class);
return Methods.remove(Class.getClass().getMethod(MethodName, Object.class, Object.class));
}
catch (NoSuchMethodException error) { throw new RuntimeException("No such method, or correct overload of method, exists."); }
}
public void ClearListeners()
{
Methods.clear();
DeclaringClasses.clear();
}
public void Raise(Object Sender, Object Arguments)
{
for(int i = 0; i < Methods.size(); i++)
{
try
{ Methods.get(i).invoke(DeclaringClasses.get(i), Sender, Arguments); }
catch(IllegalAccessException error)
{ }
catch(InvocationTargetException error)
{ throw new RuntimeException("Error in event handler"); }
}
}
}
EDIT: Revised code (@Peter Taylor)
package puppy.hanii.library;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class Event
{
public Event()
{ methods = new ArrayList<MethodClassPairing>(); }
class MethodClassPairing
{
public MethodClassPairing(Object passedContainingClass, Method passedMethod)
{
if(passedContainingClass == null || passedMethod == null)
throw new RuntimeException("passedContainingClass or passedMethod were null.");
else
{
containingClass = passedContainingClass;
method = passedMethod;
}
}
public Object containingClass;
public Method method;
@Override
public boolean equals(Object PassedObject)
{
if(PassedObject.getClass() != this.getClass() || PassedObject == null)
return false;
else
{
MethodClassPairing comparedPairing = (MethodClassPairing)PassedObject;
return (containingClass == comparedPairing.containingClass) && (method.equals(comparedPairing.method));
}
}
@Override
public int hashCode()
{
int hash = 5;
hash = 37 * hash + (this.containingClass != null ? this.containingClass.hashCode() : 0);
hash = 37 * hash + (this.method != null ? this.method.hashCode() : 0);
return hash;
}
}
List<MethodClassPairing> methods;
public boolean AddListener(Object ListeningClass, String MethodName)
{
try
{
if(!ListeningClass.getClass().getMethod(MethodName, Object.class, Object.class).isAccessible())
throw new RuntimeException(MethodName + "(Object, " + "); is not accessible.");
else
{
MethodClassPairing method = new MethodClassPairing(ListeningClass, ListeningClass.getClass().getMethod(MethodName, Object.class, Object.class));
if(methods.contains(method))
return false;
else
{
methods.add(method);
return true;
}
}
}
catch (NoSuchMethodException error) { throw new RuntimeException("No such method, or correct overloads of method, exists."); }
}
public boolean RemoveListener(Object ListeningClass, String MethodName)
{
try { return methods.remove(new MethodClassPairing(ListeningClass, ListeningClass.getClass().getMethod(MethodName, Object.class, Object.class))); }
catch (NoSuchMethodException error) { throw new RuntimeException("No such method, or correct overloads of method, exists."); }
}
public void ClearListeners()
{ methods.clear(); }
public void Raise(Object Sender, Object Arguments)
{
for(int i = 0; i < methods.size(); i++)
{
try { methods.get(i).method.invoke(methods.get(i).containingClass, Sender, Arguments); }
catch (IllegalAccessException error)
{ throw new RuntimeException("This shouldn't have happened ಠ_ಠ"); }
catch (InvocationTargetException error)
{ throw new RuntimeException("Error in event handler"); }
}
}
}