Unique classes in generic list - java

I have a generic class with a generic list in it. I want to ensure that the generic list only contains unique classes.
What I have done so far is to compare the class names with reflection (getClass()). But I think that's not a clean solution. Are there any better practices to check?
public class MyGenericClass<T extends MyGenericClass.MyInterface> {
private List<T> members = new ArrayList<>(0);
public void add(T t) {
final boolean[] classInMembers = {false};
members.forEach(member -> {
if (member.getClass().getName().equals(t.getClass().getName())) {
classInMembers[0] = true;
}
});
if (!classInMembers[0]) {
members.add(t);
}
}
public interface MyInterface {
void doSomething(String text);
}
}
public class Main {
public static void main(String[] args) {
MyGenericClass<MyGenericClass.MyInterface> myGenericClass = new MyGenericClass<>();
myGenericClass.add(new Performer1());
myGenericClass.add(new Performer2());
myGenericClass.add(new Performer3());
myGenericClass.add(new Performer3()); // should not be inserted!
}
private static class Performer1 implements MyGenericClass.MyInterface {
#Override
public void doSomething(String text) {
text = "Hi, I am performer 1!";
}
}
private static class Performer2 implements MyGenericClass.MyInterface {
#Override
public void doSomething(String text) {
text = "Hi, I am performer 2!";
}
}
private static class Performer3 implements MyGenericClass.MyInterface {
#Override
public void doSomething(String text) {
text = "Hi, I am performer 3!";
}
}
}

You could subclass a java.util.Set interface implementation. It will likely be easiest to subclass java.util.AbstractSet.
By default 'Set' will compare objects by their .equals() method - In your case, this is not sufficient. You will need to override the contains method to ensure that only instances of a unique class are added.
In your overrideen contains, it's probably the same / easier to compare class instances rather than their stringified package name
I.e. use a.getClass() == b.getClass(), rather than a.getClass().getName()

Don't use a List, use a java.util.Set instead.
A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element.
If the iteration order is important or if you want to use a custom Comparator, the TreeSet implementation can be used:
A NavigableSet implementation based on a TreeMap. The elements are ordered using their natural ordering, or by a Comparator provided at set creation time, depending on which constructor is used.
Example of a Set using a Comparator:
class MyComparator implements Comparator<Object> {
#Override
public int compare(Object e1, Object e2) {
if (e1.getClass() == e2.getClass())
return 0;
//if you wish to have some extra sort order
return e1.getClass().getName().compareTo(e2.getClass().getName());
}
}
. . .
Set mySet = new TreeSet<Object>(new MyComparator());
mySet.add(new Object());
mySet.add(new Object());//same class already in set
mySet.add("wtf");
//mySet.size() is now 2 - the second "new Object()" was not inserted due to the comparator check

Why so complicated?
public class Main {
public static void main(String[] args) {
final Class<?> helloClass = "Hello".getClass();
final Class<?> worldClass = "World".getClass();
final Class<?> intClass = Integer.class;
System.out.println(helloClass.equals(worldClass)); // -> true
System.out.println(helloClass.equals(intClass)); // -> false
}
}

You could maintain a roster of members in a Set.
public static class MyGenericClass<T extends MyGenericClass.MyInterface> {
private List<T> members = new ArrayList<>(0);
// Add this.
private Set<Class<?>> roster = new HashSet<>();
public void add(T t) {
if (!roster.contains(t.getClass())) {
members.add(t);
roster.add(t.getClass());
}
}
private void soundOff() {
for (T t : members) {
t.doSomething();
}
}
public interface MyInterface {
void doSomething();
}
}
private static class Performer implements MyGenericClass.MyInterface {
final int n;
public Performer(int n) {
this.n = n;
}
#Override
public void doSomething() {
System.out.println("Hi, I am a " + this.getClass().getSimpleName() + "(" + n + ")");
}
}
private static class Performer1 extends Performer {
public Performer1(int n) {
super(n);
}
}
private static class Performer2 extends Performer {
public Performer2(int n) {
super(n);
}
}
private static class Performer3 extends Performer {
public Performer3(int n) {
super(n);
}
}
public void test() {
MyGenericClass<MyGenericClass.MyInterface> myGenericClass = new MyGenericClass<>();
myGenericClass.add(new Performer1(1));
myGenericClass.add(new Performer2(2));
myGenericClass.add(new Performer3(3));
myGenericClass.add(new Performer3(4)); // should not be inserted!
myGenericClass.soundOff();
}

You could implement a Wrapper which provides the necessary comparison and add the wrapped instance to the set. This way you don't have to override equals and hashcode in your concrete Performer classes and you don't have to subclass a concrete Set implementation (which you are coupled to. When you subclass a HashSet, you have to use that concrete class. But what if you want to use a LinkedHashSet at some point? You have to override LinkedHashSet as well) , which may be fragile since you have to make sure that the overridden method is consistent with the rest of the class.
class MyGenericClass<T extends MyInterface> {
private Set<ClassCompareWrapper<T>> members = new HashSet<>();
public void add(T t) {
members.add(new ClassCompareWrapper<T>(t));
}
}
class ClassCompareWrapper<T> {
T t;
public ClassCompareWrapper(T t) {
this.t = t;
}
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof ClassCompareWrapper))
return false;
ClassCompareWrapper<?> that = (ClassCompareWrapper<?>) o;
return Objects.equals(t.getClass(), that.t.getClass());
}
#Override
public int hashCode() {
return Objects.hash(t.getClass());
}
#Override
public String toString() {
return "Wrapper{" +
"t=" + t +
'}';
}
}

Here are a few other ideas.
Using streams:
public void add(T t) {
if (!members.stream().anyMatch(m -> m.getClass() == t.getClass())) {
members.add(t);
}
}
Using AbstractSet and HashMap:
class ClassSet<E> extends AbstractSet<E> {
private final Map<Class<?>, E> map = new HashMap<>();
#Override
public boolean add(E e) {
// this can be
// return map.putIfAbsent(e.getClass(), e) != null;
// in Java 8
Class<?> clazz = e.getClass();
if (map.containsKey(clazz)) {
return false;
} else {
map.put(clazz, e);
return true;
}
}
#Override
public boolean remove(Object o) {
return map.remove(o.getClass()) != null;
}
#Override
public boolean contains(Object o) {
return map.containsKey(o.getClass());
}
#Override
public int size() {
return map.size();
}
#Override
public Iterator<E> iterator() {
return map.values().iterator();
}
}
A HashMap could also be used without wrapping it in a Set. The Set interface is defined around equals and hashCode, so any implementation which deviates from this is technically non-contractual. Additionally, you might want to use LinkedHashMap if the values are iterated often.

Related

Java ASM method override check

I have a problem with method override checks. I can detect simple override relations, but if the parent class has generics and the abstract method uses type parameters (return value/args), my code breaks down because the method description is not equal to the checked method.
Example:
public interface ISetting<T> {
public T method();
}
public class Setting implements ISetting<Integer> {
public Integer method() {
//Something
}
}
In ISetting, the method description is ()Ljava/lang/Object;
and in Setting, the method description is ()Ljava/lang/Integer;
How I can check this Override ?
On my head no thoughts come, how I can make this >~< All ideas which come to my head are bad (example: ignore check on desc, but overload method just break this idea)
Note that your issue does not only apply to generic supertype. You can also override a method with a more specific return type, with no Generics involved, e.g.
interface SomeInterface {
Object method();
}
class SomeImplementation implements SomeInterface {
#Override
public Integer method() {
return null;
}
}
You have to understand the concept of bridge methods.
A bridge method performs the task of overriding a method on the byte code level, having exactly the same parameter types and return type as the overridden method, and delegates to the actual implementation method.
Since the bridge method only consists of this invocation instruction, some type casts if required, and the return instruction, it is easy to parse such a method to find the actual method it belongs to, without dealing with the complex rules of the Generic type system.
Using, the following helper classes
record MethodSignature(String name, String desc) {}
record MethodInfo(int access, String owner, String name, String desc) {
MethodSignature signature() {
return new MethodSignature(name, desc);
}
}
final class MethodAndBridges {
MethodInfo actual;
final List<MethodInfo> bridges = new ArrayList<>();
MethodAndBridges(MethodSignature sig) {}
void set(MethodInfo mi) {
if(actual != null) throw new IllegalStateException();
actual = mi;
}
void addBridge(MethodInfo mi) {
bridges.add(mi);
}
}
We can gather the information in a form ready for checking override relations with the ASM library as follows:
class MethodCollector extends ClassVisitor {
static Map<MethodSignature, MethodAndBridges> getMethods(ClassReader cr) {
MethodCollector mc = new MethodCollector();
cr.accept(mc, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return mc.found;
}
final Map<MethodSignature, MethodAndBridges> found = new HashMap<>();
String owner, superClass;
List<String> interfaces;
protected MethodCollector() {
super(Opcodes.ASM9);
}
#Override
public void visit(int version, int acc,
String name, String sig, String superName, String[] ifNames) {
owner = name;
superClass = superName;
this.interfaces = ifNames == null? List.of(): List.of(ifNames);
}
#Override
public MethodVisitor visitMethod(
int acc, String name, String desc, String sig, String[] exceptions) {
MethodInfo mi = new MethodInfo(acc, owner, name, desc);
if((acc & Opcodes.ACC_BRIDGE) == 0) {
found.computeIfAbsent(mi.signature(), MethodAndBridges::new).set(mi);
return null;
}
return new MethodVisitor(Opcodes.ASM9) {
#Override public void visitMethodInsn(
int op, String owner, String name, String tDesc, boolean i) {
found.computeIfAbsent(new MethodSignature(name, tDesc),
MethodAndBridges::new).addBridge(mi);
}
};
}
}
To demonstrate how this work, let’s enhance your example, to address more cases
interface SupplierOfSerializable {
Serializable get();
}
interface ISetting<T extends CharSequence> extends Supplier<T>, Consumer<T> {
T get();
#Override void accept(T t);
Number method(int i);
static void method(Object o) {}
private void method(Number n) {}
}
class Setting implements ISetting<String>, SupplierOfSerializable {
public String get() {
return "";
}
#Override
public void accept(String t) {}
public Integer method(int i) {
return i;
}
static void method(Object o) {}
void method(Number n) {}
}
and check the override relations (only considering the direct interfaces, without recursion)
public class CheckOverride {
public static void main(String[] args) throws IOException {
MethodCollector mc = new MethodCollector();
new ClassReader(Setting.class.getName())
.accept(mc, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
Map<MethodSignature, MethodAndBridges> implMethods = mc.found;
Map<MethodInfo, Set<MethodInfo>> overrides = new HashMap<>();
for(String ifType: mc.interfaces) {
Map<MethodSignature, MethodAndBridges> ifMethods
= MethodCollector.getMethods(new ClassReader(ifType));
System.out.println("interface " + ifType.replace('/', '.'));
printMethods(ifMethods);
System.out.println();
ifMethods.values().removeIf(CheckOverride::nonOverridable);
implMethods.forEach((sig, method) -> {
if(nonOverridable(method)) {
overrides.putIfAbsent(method.actual, Set.of());
return;
}
var overridden = ifMethods.get(sig);
if(overridden == null && method.bridges.isEmpty()) {
overrides.putIfAbsent(method.actual, Set.of());
return;
}
Set<MethodInfo> set = overrides.compute(method.actual,
(k, s) -> s == null || s.isEmpty()? new HashSet<>(): s);
if(overridden != null) set.add(overridden.actual);
for(var mi: method.bridges) {
overridden = ifMethods.get(mi.signature());
if(overridden != null) set.add(overridden.actual);
}
});
}
System.out.println("class " + mc.owner.replace('/', '.'));
printMethods(implMethods);
System.out.println();
System.out.println("Final result");
System.out.println("class " + mc.owner.replace('/', '.'));
overrides.forEach((m,overridden) -> {
System.out.println(" " + toDeclaration(m, false));
if(!overridden.isEmpty()) {
System.out.println(" overrides");
overridden.forEach(o ->
System.out.println(" " + toDeclaration(o, true)));
}
});
}
static boolean nonOverridable(MethodAndBridges m) {
return (m.actual.access() & (Opcodes.ACC_PRIVATE|Opcodes.ACC_STATIC)) != 0
|| m.actual.name().startsWith("<");
}
static void printMethods(Map<MethodSignature, MethodAndBridges> methods) {
methods.forEach((sig, methodAndBridges) -> {
System.out.println(" "+toDeclaration(methodAndBridges.actual,false));
if(!methodAndBridges.bridges.isEmpty()) {
System.out.println(" bridges");
for(MethodInfo mi: methodAndBridges.bridges) {
System.out.println(" " + toDeclaration(mi, false));
}
};
});
}
private static String toDeclaration(MethodInfo mi, boolean withType) {
StringBuilder sb = new StringBuilder();
sb.append(Modifier.toString(mi.access() & Modifier.methodModifiers()));
if(sb.length() > 0) sb.append(' ');
String clName = mi.owner();
var mt = MethodTypeDesc.ofDescriptor(mi.desc());
if(mi.name().equals("<init>"))
sb.append(clName, clName.lastIndexOf('/') + 1, clName.length());
else {
sb.append(mt.returnType().displayName()).append(' ');
if(withType) sb.append(clName.replace('/', '.')).append('.');
sb.append(mi.name());
}
if(mt.parameterCount() == 0) sb.append("()");
else {
String sep = "(";
for(ClassDesc cd: mt.parameterList()) {
sb.append(sep).append(cd.displayName());
sep = ", ";
}
sb.append(')');
}
return sb.toString();
}
}
interface ISetting
public static void method(Object)
public abstract void accept(CharSequence)
bridges
public void accept(Object)
public abstract Number method(int)
private void method(Number)
public abstract CharSequence get()
bridges
public Object get()
interface SupplierOfSerializable
public abstract Serializable get()
class Setting
Setting()
public Integer method(int)
bridges
public Number method(int)
public void accept(String)
bridges
public void accept(Object)
public void accept(CharSequence)
static void method(Object)
public String get()
bridges
public Object get()
public CharSequence get()
public Serializable get()
void method(Number)
Final result
class Setting
public String get()
overrides
public abstract Serializable SupplierOfSerializable.get()
public abstract CharSequence ISetting.get()
Setting()
public Integer method(int)
overrides
public abstract Number ISetting.method(int)
public void accept(String)
overrides
public abstract void ISetting.accept(CharSequence)
void method(Number)
static void method(Object)
The code uses newer Java features, like var, record, and the constant API, but I think, the result is straight-forward enough for converting it to older Java versions, if really required.

Iterator of iterable object should return the iterable object itself in Java

Suppose you have given a class in Java that extends the Iterable interface. This class has to provide an Iterator that should return the instance of the surrounding class, take a look at the main method.
public class Test implements Iterable<Test> {
#Override
public Iterator<Test> iterator() {
return new Iterator<Test>() {
private boolean onlyOnce = false;
#Override
public boolean hasNext() {
return false;
}
#Override
public Test next() {
if (!onlyOnce) {
onlyOnce = true;
// TODO return
}
throw new NoSuchElementException("Iterator has already been called");
}
};
}
public static void main(String[] args) {
Test test = new Test();
Test test2 = test.iterator().next();
boolean b = test == test2; // should be true
}
}
How could this issue be solved in Java?
In order to return the enclosing instance of Test, use a qualified this:
return Test.this;
However, a much neater way to implement the method would be to use an existing iterator implementation:
#Override
public Iterator<Test> iterator() {
return Arrays.asList(this).iterator();
// or Collections.singleton(this).iterator()
// or Stream.of(this).iterator()
// or many other possibilities.
}

OO: Does container contain bike or chair?

A container may contain bikes and chairs, both belonging to a person. I would like to check, if the container contains either bikes or chairs of said person. Is this possible without using instanceof?
public class Container {
public Map<Person, List<Item>> items = new HashMap<>();
public void add(Person p, Item item) {
items.get(p).add(item);
}
public boolean containsChair(Person owner) {
for(Item i : items.get(owner)) {
if(i instanceof Chair) {
return true;
}
}
return false;
}
public boolean containsBike(Person owner) {
for(Item i : items.get(owner)) {
if(i instanceof Bike) {
return true;
}
}
return false;
}
}
For the purpose of illustration, Item, Bike, Chair, Person are all simplest class stubs:
public class Person { public String name; }
public abstract class Item {}
public class Bike extends Item { public Wheel[] wheels;}
public class Chair extends Item { public Leg[] legs;}
public class Wheel {}
public class Leg {}
In the runner, a Person should be able to add Chairs and Bikes to its container:
import java.util.ArrayList;
public class Runner {
public static void main(String[] args) {
Container c = new Container();
Person p = new Person();
// Prevent null pointer exception
c.items.put(p, new ArrayList<>());
c.add(p, new Chair());
// True
System.out.println(c.containsChair(p));
}
}
You could add to class Item an abstract method ItemType getType(). ItemType would be an enum enumerating all possible item types.
public abstract class Item {
public abstract ItemType getType();
}
public enum ItemType {
BIKE, CHAIR;
}
Implementation of Chair:
public static class Chair extends Item {
public Leg[] legs;
#Override
public ItemType getType() {
return ItemType.CHAIR;
}
}
Then you could define a contains method to search for a the given Person if it has an item with a certain ItemType:
public boolean contains(Person owner, ItemType itemType) {
return items.get(owner).stream().anyMatch(item ->itemType.equals(item.getType()));
}
Or null-safe regarding the owners items list:
public boolean contains(Person owner, ItemType itemType) {
return Optional.ofNullable(items.get(owner))
.map(i -> i.stream().anyMatch(item -> itemType.equals(item.getType())))
.orElse(false);
}
Usage:
public static void main(String[] args) {
Container c = new Container();
Person p = new Person();
// Prevent null pointer exception
c.items.put(p, new ArrayList<>());
c.add(p, new Chair());
// True
System.out.println(c.contains(p, ItemType.CHAIR));
}
EDIT
Following this approach there is no need for instanceof checks. The usage of instanceof can be a hint indicating that the design has some flaws.
You can store Bike and Chair in two different datastructure.
public final class Container {
private final Map<Person, List<Chair>> chairs = new HashMap<>();
private final Map<Person, List<Bike>> bikes = new HashMap<>();
public void add(Person p, Chair chair) {
chairs.putIfAbsent(p, new ArrayList<Chair>());
chairs.get(p).add(chair);
}
public void add(Person p, Bike bike) {
bikes.putIfAbsent(p, new ArrayList<Bike>());
bikes.get(p).add(bike);
}
public boolean containsChair(Person owner) {
return chairs.getOrDefault(owner, Collections.emptyList()).size() > 0;
}
public boolean containsBike(Person owner) {
return bikes.getOrDefault(owner, Collections.emptyList()).size() > 0;
}
}
Note that I also made your instance fields private to hide the fact that data is stored in a Map and avoid the runner code to have the responsibility to instanciate an ArrayList if not existant. Both the class and its fields are also final to achieve a better immutability. Both encapsulation and immutability are considered good practices when doing OOP.
Usage
public static void main(String[] args) {
Container c = new Container();
Person p = new Person();
c.add(p, new Chair());
System.out.println(c.containsChair(p)); //true
System.out.println(c.containsBike(p)); //false
}
What I ended up doing was to add two methods to Item:
public boolean containsBike() {return false;}
public boolean containsChair() {return false;}
While this certainly could be optimized, the check is now done by calling the method of the object:
public boolean containsBike(Person p) {
boolean hasBike = false;
// Prevent NullPointerException
if(containsSomethingOf(p)) {
for(Item i : items.get(p)) {
if(i != null) {
if (i.containsBike()) {
hasBike = true;
}
}
}
}
return hasTrousers;
}
I think this is what is called polymorphism.

Set ordered by "add() count"

I'm trying to implement a Set which is ordered by the count of additions like this:
public class App {
public static void main(String args[]) {
FrequencyOrderedTreeSet<String> set = new FrequencyOrderedTreeSet<String>();
set.add("bar");
set.add("foo");
set.add("foo");
Iterator<String> i = set.iterator();
while (i.hasNext()) {
System.out.print(i.next());
}
// prints "foobar"
}
}
I've created a protected class FrequencyOrderedTreeSet.Element which implements Comparable and has a T entry and an int frequency property and extended TreeSet<FrequencyOrderedTreeSet.Element> with FrequencyOrderedTreeSet<T> and overrode the compareTo and equals methods on the Element.
One problem is that I can't override the add() method because of type erasure problems and also I can't call instanceof Element in the equals method, because in case object given to it is an Element, I have to compare their entries, but if it's not, I have to compare the object itself to this.entry.
In the add method I create a new element, find the element with the same entry in the set, set the frequency on the new element to "old+1", remove the old one and add the new one. I'm not even sure this is the best way to do this or if it would work even because the other problems I described.
The question is: what's the best way to implement such data structure? In case I'm somehow on the right track - how can I circumvent the problems I've mentioned above?
Here's a basic implementation. It's not the most optimal and will take some more work if you want to implement the full Set interface.
public class FrequencySet<T> implements Iterable<T>
{
private TreeSet<T> set;
private HashMap<T, Integer> elements = new HashMap<T, Integer>();
public FrequencySet()
{
set = new TreeSet<T>(new Comparator<T>()
{
public int compare(T o1, T o2)
{
return elements.get(o2)-elements.get(o1);
}
});
}
public void add(T t)
{
Integer i = elements.get(t);
elements.put(t, i == null ? 1 : i+1);
set.remove(t);
set.add(t);
}
public Iterator<T> iterator() {return set.iterator();}
public static void main(String [] args)
{
FrequencySet<String> fset = new FrequencySet<String>();
fset.add("foo");
fset.add("bar");
fset.add("foo");
for (String s : fset)
System.out.print(s);
System.out.println();
fset.add("bar");
fset.add("bar");
for (String s : fset)
System.out.print(s);
}
}
The key is in the add method. We change the counter for the given object (which changes the relation order), remove it from the backing set and put it back in.
This works the other way (count is increased when you use GET)
#SuppressWarnings("rawtypes")
final class Cache implements Comparable {
private String key;
private String value;
private int counter;
public String getValue() {
counter++;
return value;
}
private void setValue(String value) { this.value = value; }
public String getKey() { return key; }
private void setKey(String key) { this.key = key; }
public int getCounter() { return counter; }
public void setCounter(int counter) { this.counter = counter; }
public Cache(String key, String value) {
this.setKey(key);
this.setValue(value);
setCounter(0);
}
#Override
public int compareTo(Object arg0) {
if(!(arg0 instanceof Cache)) {
throw new ClassCastException();
}
return this.getCounter() - ((Cache) arg0).getCounter();
}
}

A generic observer pattern in Java

The java.util.Observer and java.util.Observable are ugly. They require the sorts of casts that make type-safety fans uncomfortable, and you can't define a class to be an Observer of multiple things without ugly casts. In fact, in "How do I know the generic object that the Observer class sends in Java?", an answerer says that only one type of data should be used in each observer / observable.
I'm trying to make a generic version of the observer pattern in Java to get round both these problems. It's not unlike the one in the previously mentioned post, but that question was not obviously resolved (the last comment is an unanswered question from the OP).
Observer.java
package util;
public interface Observer<ObservedType> {
public void update(Observable<ObservedType> object, ObservedType data);
}
Observable.java
package util;
import java.util.LinkedList;
import java.util.List;
public class Observable<ObservedType> {
private List<Observer<ObservedType>> _observers =
new LinkedList<Observer<ObservedType>>();
public void addObserver(Observer<ObservedType> obs) {
if (obs == null) {
throw new IllegalArgumentException("Tried
to add a null observer");
}
if (_observers.contains(obs)) {
return;
}
_observers.add(obs);
}
public void notifyObservers(ObservedType data) {
for (Observer<ObservedType> obs : _observers) {
obs.update(this, data);
}
}
}
Hopefully this will be useful to someone.
I prefer using an annotation so a listener can listen to different types of events.
public class BrokerTestMain {
public static void main(String... args) {
Broker broker = new Broker();
broker.add(new Component());
broker.publish("Hello");
broker.publish(new Date());
broker.publish(3.1415);
}
}
class Component {
#Subscription
public void onString(String s) {
System.out.println("String - " + s);
}
#Subscription
public void onDate(Date d) {
System.out.println("Date - " + d);
}
#Subscription
public void onDouble(Double d) {
System.out.println("Double - " + d);
}
}
prints
String - Hello
Date - Tue Nov 13 15:01:09 GMT 2012
Double - 3.1415
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Subscription {
}
public class Broker {
private final Map<Class, List<SubscriberInfo>> map = new LinkedHashMap<Class, List<SubscriberInfo>>();
public void add(Object o) {
for (Method method : o.getClass().getMethods()) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (method.getAnnotation(Subscription.class) == null || parameterTypes.length != 1) continue;
Class subscribeTo = parameterTypes[0];
List<SubscriberInfo> subscriberInfos = map.get(subscribeTo);
if (subscriberInfos == null)
map.put(subscribeTo, subscriberInfos = new ArrayList<SubscriberInfo>());
subscriberInfos.add(new SubscriberInfo(method, o));
}
}
public void remove(Object o) {
for (List<SubscriberInfo> subscriberInfos : map.values()) {
for (int i = subscriberInfos.size() - 1; i >= 0; i--)
if (subscriberInfos.get(i).object == o)
subscriberInfos.remove(i);
}
}
public int publish(Object o) {
List<SubscriberInfo> subscriberInfos = map.get(o.getClass());
if (subscriberInfos == null) return 0;
int count = 0;
for (SubscriberInfo subscriberInfo : subscriberInfos) {
subscriberInfo.invoke(o);
count++;
}
return count;
}
static class SubscriberInfo {
final Method method;
final Object object;
SubscriberInfo(Method method, Object object) {
this.method = method;
this.object = object;
}
void invoke(Object o) {
try {
method.invoke(object, o);
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
}
A modern update: ReactiveX is a very nice API for asynchronous programming based on the Observer pattern, and it's fully generic. If you're using Observer/Observable to "stream" data or events from one place in your code to another, you should definitely look into it.
It's based on functional programming, so it looks very sleek with Java 8's lambda syntax:
Observable.from(Arrays.asList(1, 2, 3, 4, 5))
.reduce((x, y) -> x + y)
.map((v) -> "DecoratedValue: " + v)
.subscribe(System.out::println);
I once wrote a generic implementation of the observer pattern for Java using dynamic proxies. Here's a sample of how it could be used:
Gru gru = new Gru();
Minion fred = new Minion();
fred.addObserver(gru);
fred.moo();
public interface IMinionListener
{
public void laughing(Minion minion);
}
public class Minion extends AbstractObservable<IMinionListener>
{
public void moo()
{
getEventDispatcher().laughing(this);
}
}
public class Gru implements IMinionListener
{
public void punch(Minion minion) { ... }
public void laughing(Minion minion)
{
punch(minion);
}
}
The full source code of AbstractObservable is available on pastebin. Way back I blogged about how it works in a bit more detail, also referring to related projects.
Jaana wrote an interesting summary of different approaches, also contrasting the dynamic proxy approach with others. Much thanks of course goes to Allain Lalonde from which I got the original idea. I still haven't checked out PerfectJPattern, but it might just contain a stable implementation of the observer pattern; at least it seems like a mature library.
Try to use class EventBus of Guava.
You can declare a Observer like this:
public class EventObserver {
#Subscribe
public void onMessage(Message message) {
...
}
}
New a EventBus like this:
EventBus eventBus = new EventBus();
And register Observer like this:
eventBus.register(new EventObserver());
Last notify Observer like:
eventBus.post(message);
I found a similar request but it was rather on codereview.
I think it's worth mentioning it here.
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Supplier;
/**
* like java.util.Observable, But uses generics to avoid need for a cast.
*
* For any un-documented variable, parameter or method, see java.util.Observable
*/
public class Observable<T> {
public interface Observer<U> {
public void update(Observable<? extends U> observer, U arg);
}
private boolean changed = false;
private final Collection<Observer<? super T>> observers;
public Observable() {
this(ArrayList::new);
}
public Observable(Supplier<Collection<Observer<? super T>>> supplier) {
observers = supplier.get();
}
public void addObserver(final Observer<? super T> observer) {
synchronized (observers) {
if (!observers.contains(observer)) {
observers.add(observer);
}
}
}
public void removeObserver(final Observer<? super T> observer) {
synchronized (observers) {
observers.remove(observer);
}
}
public void clearObservers() {
synchronized (observers) {
this.observers.clear();
}
}
public void setChanged() {
synchronized (observers) {
this.changed = true;
}
}
public void clearChanged() {
synchronized (observers) {
this.changed = false;
}
}
public boolean hasChanged() {
synchronized (observers) {
return this.changed;
}
}
public int countObservers() {
synchronized (observers) {
return observers.size();
}
}
public void notifyObservers() {
notifyObservers(null);
}
public void notifyObservers(final T value) {
ArrayList<Observer<? super T>> toNotify = null;
synchronized(observers) {
if (!changed) {
return;
}
toNotify = new ArrayList<>(observers);
changed = false;
}
for (Observer<? super T> observer : toNotify) {
observer.update(this, value);
}
}
}
Original answer from codereview stackexchange

Categories