In Java 8, I wanted to write a class that, given one or more listeners, would return a proxied List (using implementation of List of my choosing by passing in its class) that triggers the listener whenever something is added or removed. The code (bear with me) is below:
public final class EventedList {
private EventedList() {
}
protected static class ListInvocationHandler<T> implements InvocationHandler {
private List<T> theList;
private ListChangeHandler<T>[] listeners;
public ListInvocationHandler(Class<? extends List<T>> listClass, ListChangeHandler<T>[] listeners) throws InstantiationException, IllegalAccessException {
this.listeners = listeners;
theList = listClass.newInstance();
}
public Object invoke(Object self, Method method, Object[] args)
throws Throwable {
Object ret = method.invoke(theList, args);
switch(method.getName()) {
case "add":
trigger((T)args[0], true);
break;
case "remove":
if(args[0] instanceof Integer) {
trigger((T)ret, false);
} else {
trigger((T)args[0], false);
}
break;
}
return ret;
}
public void trigger(T obj, boolean added) {
Arrays.stream(listeners).forEachOrdered(l -> l.onChange(obj, added));
}
}
public static <T, U extends List<T>> List<T> newList(Class<U> listClass, ListChangeHandler<T> ... listeners) throws IllegalArgumentException, InstantiationException, IllegalAccessException {
@SuppressWarnings("unchecked")
List<T> obj = (List<T>)Proxy.newProxyInstance(listClass.getClassLoader(), new Class<?>[]{List.class}, new ListInvocationHandler<T>(listClass, listeners));
return obj;
}
public static <T, U extends List<T>> List<T> newListSafe(Class<U> listClass, ListChangeHandler<T> ... listeners) {
List<T> obj = null;
try {
obj = newList(listClass, listeners);
} catch (IllegalArgumentException | InstantiationException
| IllegalAccessException e) {
}
return obj;
}
}
It works, but it is certainly not without its issues.
- I initially only had one type T, but I was getting errors using
Class<? extends List<T>>
so I use U to represent? extends List<T>
instead. - In invoke method of
ListInvocationHandler
, I'm having to forcively castObject
toT
. I think that is unavoidable, but I welcome any alternatives. newProxyInstance
is returningObject
that I'm having to cast toList
. Also this I believe is unavoidable but I welcome any alternatives.- I'm receiving a "Potential heap pollution via varargs parameter listeners" warning on listeners parameters presumably because they are variable parameter arguments, however I don't see the obvious risk of doing it this way.
The main that I'm using is the following:
public static void main(String[] args) {
List<String> list = EventedList.newListSafe(ArrayList.class, new ListChangeHandler<String>() {
@Override
public void onChange(String value, boolean added) {
System.out.println(value + ", " + (added ? "added" : "removed"));
}
});
list.add("Badger"); // Badger, added
list.add("Badger"); // Badger, added
list.add("Badger"); // Badger, added
list.add("Badger"); // Badger, added
list.remove("Badger"); // Badger, removed
list.add("Mushroom"); // Mushroom, added
list.remove("Mushroom"); // Mushroom, removed
// [Badger, Badger, Badger]
System.out.println(Arrays.toString(list.toArray()));
}
- The main itself calling the method has a nice type safety warning, even if the parameters should be implicit.
I apologize for the wall of text. I appreciate any input.
Aucun commentaire:
Enregistrer un commentaire