Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Not exactly. It will be safe <em>if</em> all clients of the <code>EventManager</code> class always use generics and never rawtypes; i.e., if your client code compiles without generics-related warnings.</p> <p>However, it is not difficult for client code to ignore those and insert an <code>IEventSubscriber</code> that's expecting the wrong type:</p> <pre><code>EventManager manager = ...; IEventSubscriber&lt;Integer&gt; integerSubscriber = ...; // subscriber expecting integers // casting to a rawtype generates a warning, but will compile: manager.subscribe((IEventSubscriber) integerSubscriber, String.class); // the integer subscriber is now subscribed to string messages // this will cause a ClassCastException when the integer subscriber tries to use "test" as an Integer: manager.publish("test", String.class); </code></pre> <p>I don't know of a compile-time way to prevent this scenario, but you can check the generic parameter types of instances of <code>IEventSubscriber&lt;T&gt;</code> at runtime <em>if</em> the generic type <code>T</code> was bound to the class <em>at compile time</em>. Consider:</p> <pre><code>public class ClassA implements IEventSubscriber&lt;String&gt; { ... } public class ClassB&lt;T&gt; implements IEventSubscriber&lt;T&gt; { ... } IEventSubscriber&lt;String&gt; a = new ClassA(); IEventSubscriber&lt;String&gt; b = new ClassB&lt;String&gt;(); </code></pre> <p>In the above example, for <code>ClassA</code>, <code>String</code> is bound to the parameter <code>T</code> at compile time. All instances of <code>ClassA</code> will have <code>String</code> for the <code>T</code> in <code>IEventSubscriber&lt;T&gt;</code>. But in <code>ClassB</code>, <code>String</code> is bound to <code>T</code> at runtime. Instances of <code>ClassB</code> could have any value for <code>T</code>. If your implementations of <code>IEventSubscriber&lt;T&gt;</code> bind the parameter <code>T</code> at compile time as with <code>ClassA</code> above, then you can obtain that type at runtime the following way:</p> <pre><code>public &lt;T&gt; boolean subscribe(IEventSubscriber&lt;T&gt; subscriber, Class&lt;T&gt; eventClass) { Class&lt;? extends IEventSubscriber&lt;T&gt;&gt; subscriberClass = subscriber.getClass(); // get generic interfaces implemented by subscriber class for (Type type: subscriberClass.getGenericInterfaces()) { ParameterizedType ptype = (ParameterizedType) type; // is this interface IEventSubscriber? if (IEventSubscriber.class.equals(ptype.getRawType())) { // make sure T matches eventClass if (!ptype.getActualTypeArguments()[0].equals(eventClass)) { throw new ClassCastException("subscriber class does not match eventClass parameter"); } } } CopyOnWriteArraySet&lt;IEventSubscriber&gt; existingSubscribers = subscriptions.putIfAbsent(eventClass, new CopyOnWriteArraySet&lt;IEventSubscriber&gt;()); return existingSubscribers.add(subscriber); } </code></pre> <p>This will cause the types to be checked when the subscriber is registered with the <code>EventManager</code>, allowing you to track down bad code more easily, rather than if the types were to just get checked much later when publishing an event. However, it does do some hokey reflection and can only check the types if <code>T</code> is bound at compile time. If you can trust the code that will be passing subscribers to EventManager, I'd just leave the code as it is, because it's much simpler. However checking the type using reflection as above will make you a <em>little</em> safer IMO.</p> <p>One other note, you may want to refactor the way you initialize your <code>CopyOnWriteArraySet</code>s, because the <code>subscribe</code> method is currently creating a new set on every invocation, whether it needs to or not. Try this:</p> <pre><code>CopyOnWriteArraySet&lt;IEventSubscriber&gt; existingSubscribers = subscriptions.get(eventClass); if (existingSubscribers == null) { existingSubscribers = subscriptions.putIfAbsent(eventClass, new CopyOnWriteArraySet&lt;IEventSubscriber&gt;()); } </code></pre> <p>This avoids creating a new <code>CopyOnWriteArraySet</code> on every method call, but if you have a race condition and two threads try to put in a set at once, <code>putIfAbsent</code> will still return the first set created to the second thread, so there is no danger of overwriting it.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload