4 ms·
> In this example, Java is rather annoying, especially when it comes to testing the type of a message with instanceof and casting objects from one type to anoth
by spopejoy 12y ago
> In this example, Java is rather annoying, especially when it comes to testing the type of a message with instanceof and casting objects from one type to another.
Using instanceof is an antipattern 99% of the time, easily avoided with proper API design. In this case a simple enum type would help with appropriate getType() method on the event; or a polymorphic event API with dedicated method types for each event (LifecycleEvent marker interface with ExitEvent subtype containing onExitEvent(ExitMessage m), etc), or ... or ...
Writing an intro to Java + touting your company's API + blaming Java for yourco's API problems: seems like bad form.
Which reminds me: can someone explain how actors is any different than using messages and queues for concurrent programming? Unless it also implies "magic translation of messages to method calls" which makes me uncomfortable (bad memories of CORBA/Web services)
- EdwardDiego 12y ago> or a polymorphic event API with dedicated method types for each event Exactly!
- sivanmz 12y agoAgree that instanceof is wrong and discredits the article. I'm not sure if this is what you meant, but here is how polymorphism along with the double dispatch pattern would work: NaiveActor#handleLifecycleMessage will need to delegate to the event subclass, which will in turn select the appropriate method on NaiveActor. Example of subclass of LifecycleMessage: class ExitMessage implements LifecycleMessage { @Override void handle(BasicActor actor) { actor.handleExitMessage(this); } } In NaiveActor: @Override protected void handleLifecycleMessage(LifecycleMessage m) { m.handle(this); } @Override protected void handleExitMessage(ExitMessage m) { if (Objects.equals(m.getActor(), myBadActor) { System.out.println("My bad actor has just died of '" + m.getCause() + "'. Restarting."); spawnBadActor(); } return super.handleExitMessage(m); } It's been my experience that many developers resort to instanceof or enum type flags because they don't believe polymorphism actually works in real world situations such as this.
- mbrock 12y agoI think a lot of programmers aren't totally clear about how dispatch works in Java. I've talked to many who couldn't explain exactly why it doesn't work to overload based on subtype information only known at runtime. Type flags are extremely obvious!
- spopejoy 12y agoThere's something wrong with this approach, though: it couples ExitMessage and BasicActor, in that BasicActor would have to have a method for each event type, resulting in lots of empty methods, etc. What I'm describing is a marker interface for eventing, with specific subtype APIs: interface LifecycleListener { } interface StartListener extends LifecycleListener { void onStart(Foo f); } interface ExitListener extends LifecycleListener { void onEnd(Foo f); } along with a single registration method (perhaps a LifecycleObservable API, or in a class also offering event-firing methods): Disposable addLifecycleListener(LifecycleListener l) The benefits to the client code are numerous: clarity (dedicated methods for events), declarative code ('implements' section documents interactions), performance (no dispatching in client code). [Note, Disposable here represents a dispose() function to deregister the listener, avoiding the classic pair of void register/deregister methods. Often I find void methods represent a missed opportunity ... wishing Java was more like Smalltalk here] The service side has to jump through some hoops to efficiently dispatch, using class equality or instanceof at registration time to pre-select listeners. This code could certainly use pattern-matching but personally I think it would look identical. If anything, I'm glad that pattern-matching isn't available in this case, to at least alert framework devs to the problem and not push it off to tons of switching on the client.
- heavenlyhash 12y agoI believe the segment that cast shade on instanceof checks was doing so in the process of mentioning "pattern matching" features from other languages. It would be difficult to suggest the utility of pattern matching without explaining the issue that it addresses. I agree that you can do this with a boilerplate of `MyEnum getType()` methods, and I admit I often do this so I can use a switch statement in situations like these, but I don't at all resent the suggestion that maybe there's a better way -- this does come up often enough that I would rather appreciate a language feature that helped me deal with it.