Composition vs Inheritance: Converting Java’s Swing Timer class to Scala

Posted by – July 27, 2011

I’ve been having a look at using Swing in Scala. Java’s Swing is messy, verbose, buggy, and unintuitive. Scala sweetens things a bit, although if we’re to believe the rumours from the EPFL, the Observer Pattern, around which Swing is based, is more of an anti-pattern and should be deprecated. So maybe Swing’s days are numbered, but in the current absence of anything better, I’m diving in.

Scala has cleaned up some of the names and classes to make things a bit simpler, but there are some things missing, and Timer is one of them. This isn’t too much of a big deal, because you can still just use the Java version, but it would be nice if you could treat it in the same way as you do button clicks, using a Publisher / Reactor model, rather than Java’s Listeners (more details here).

So to bring up a Frame with a coloured background that changes according to a Timer, I did this:

import swing._
import java.awt.{Graphics, Color}
import java.awt.event.{ActionEvent, ActionListener}
import javax.swing.Timer

object ColorPanel extends SimpleSwingApplication {
  var c: Color = new Color(0)

  def top = new MainFrame {
    title = "Flash!"
    contents = p
  }

  val p = new Panel with ActionListener {
    preferredSize = new Dimension(200, 200)

    override def paintComponent(g: Graphics2D) {
      g.setColor(c)
      g.fillRect(0, 0, size.width, size.height)
    }

    def actionPerformed(e: ActionEvent) {
      c = new Color((c.getRGB() + 1000) % 16777216)
      repaint
    }
  }

  val timer = new Timer(100, p)
  timer.start()
}

That should be pretty simple to understand. Our application is a SimpleSwingApplication, which requires the top window to be specified in a method called top. We then define a Panel, and override the paintComponent method, just as in Java. We implement ActionListener‘s actionPerformed method, and here goes what we want the panel to do when it receives an event (i.e. change colour and repaint). Finally we specify the Timer and start it.

In Scala Swing, to react to an event, you listenTo a Publisher. So we want to make a Java Timer into a Publisher.

I started by making my ScalaTimer a Component, because you get Publisher / Reactor functionality for free. (This doesn’t really make sense, and actually all we need to do is to implement the Publisher trait.) The other way I thought of was to directly extend Java’s Timer class – although I wasn’t convinced this was the way, because the provided classes don’t seem to subclass their Java peers (they do some kind of wrapping with a peer value, which I don’t understand). Unsure which is best, I tried both.

1) Wrapping it (composition)

case class TimerEvent (source: AnyRef) extends swing.event.Event

class ScalaTimer(delayTime: Int) extends swing.Publisher {
  outer =>

  private val t = new javax.swing.Timer(delayTime, new java.awt.event.ActionListener {
    def actionPerformed(e: java.awt.event.ActionEvent) {
      publish(new TimerEvent(outer))
    }
  })

  def addActionListener(listener: java.awt.event.ActionListener) {t.addActionListener(listener)}
  def fireActionPerformed(e: swing.event.ActionEvent) {publish(e)}
  def actionCommand: String = t.getActionCommand
  def actionListeners: Array[ java.awt.event.ActionListener ] = t.getActionListeners
  def delay: Int = t.getDelay
  def initialDelay = t.getInitialDelay
  def listeners[A <: java.util.EventListener](listenerType: Class[A]) = t.getListeners(listenerType)
  def coalesce: Boolean = t.isCoalesce
  def repeats: Boolean = t.isRepeats
  def running: Boolean = t.isRunning
  def removeActionListener(listener: java.awt.event.ActionListener) {t.removeActionListener(listener)}
  def restart() {t.restart()}
  def actionCommand_=(command: String) {t.setActionCommand(command)}
  def coalesce_=(flag: Boolean) {t.setCoalesce(flag)}
  def delay_=(delay: Int) {t.setDelay(delay)}
  def initialDelay_=(initialDelay: Int) {t.setInitialDelay(initialDelay)}
  def repeats_=(flag: Boolean) {t.setRepeats(flag)}
  def start() {t.start()}
  def stop() {t.stop()}
}


object ScalaTimer {
  import javax.swing.Timer
  def getLogTimers: Boolean = Timer.getLogTimers
  def setLogTimers(flag: Boolean) {Timer.setLogTimers(flag)}
}

If that’s confusing, here are some explanations:

  • outer => is an alias which is a self-reference. It’s like this, except this isn’t always in scope, i.e. in inner classes, so we need to define an alias for the outer scope specifically
  • We instantiate a private Timer t, giving it an anonymous ActionListener, which publishes a TimerEvent whenever it gets an ActionEvent from t
  • We have to make our own Event type, because Scala’s ActionEvents require a Component to be specified as their source, and ScalaTimer isn’t a Component
  • I used fully qualified names to help avoid confusion, since there are both Scala and Java ActionEvents
  • I changed the method names to fit the Scala conventions, so instead of myTimer.getDelay(), we have myTimer.delay, and instead of myTimer.setRepeats(true), we state myTimer.repeats = true. Personally I think the _= syntactic sugar for setters can be confusing, since it’s syntactically the same as assigning a value to a field, so while you might think you’ve set a field to equal your object, it’s almost certainly done something else, like copying values.
  • We have a companion object at the end which allows us to include Timer’s static methods

Overall this was pretty straightforward, once I’d found out how to use aliases, although it involved quite a bit of typing, copying the methods from Timer’s javadoc.

2) Extending javax.swing.Timer (inheritance)

class ScalaTimer2 (delay: Int)
      extends Timer(delay, new ActionListener { def actionPerformed(e: ActionEvent) {} })
      with swing.Publisher {
  outer =>

  removeActionListener(getActionListeners.apply(0))

  addActionListener(new ActionListener {
    def actionPerformed(e: ActionEvent) {
      publish(new TimerEvent(outer))
    }
  })
}

object ScalaTimer2 {... same as ScalaTimer ...}

OK, this was a little tricky. Coming from a Java background, Scala’s constructors are a bit weird. If the base class constructor contains arguments, you have to pass these in your subclass signature, and the only values you have available are from the subclass’s arguments. Timer has only one constructor, which takes two arguments, an integer delay and an ActionListener. So we need to supply an ActionListener either by taking one in the main argument list, or generating one, as above.

Unfortunately neither a reference to self or the publish method will be available to pass in, because the object doesn’t yet exist. So above, we pass a dummy ActionListener which we then remove, then add a real one containing the publish method.

I also did a version using an auxiliary constructor taking just an Int, which I shortened to the above. Then I made the primary constructor private, similar to the example below.

Postscript: Actually it’s easier than this. The Timer you send to the Java constructor can be null. So all you need is:

class ScalaTimer2 (delay: Int) extends Timer(delay, null) with Publisher {
  outer =>
  addActionListener(new ActionListener {
    def actionPerformed(e: ActionEvent) {
      publish(new TimerEvent(outer))
    }
  })
}

Just add a companion object containing those pesky static methods, and that’s it!

3) Extending Timer, but using a factory method

Notice that adding a dummy ActionListener is a workaround rather than a solution, and in the general case, such methods won’t be available. Instead, we can avoid the constructor and use a factory method. Kipton Barros on StackOverflow showed me how to do this, using the companion object’s apply method, so a new timer can be invoked just like with a constructor, except without the new keyword.

class ScalaTimer3 private (delay: Int, listener: java.awt.event.ActionListener)
      extends javax.swing.Timer(delay, listener) with swing.Publisher {
  // no body!
}

object ScalaTimer3 {
  def apply(delay: Int): ScalaTimer3 = {
    lazy val ret: ScalaTimer3 = new ScalaTimer3(delay, new java.awt.event.ActionListener {
      def actionPerformed(e: java.awt.event.ActionEvent) {
        ret.publish(TimerEvent(ret))
      }
    })
    ret
  }
  // + static methods here
}

This cleverly uses a lazy val, which (for reasons not entirely understood) allows reference to itself within the inner class.

Note also I’ve added the private keyword to the primary constructor, so that only the object’s apply method is available.

Conclusion

So which solution is best? Well, given infinite time and patience, I would favour the first solution, where we compose rather than inherit. It’s the most flexible, and allowed us to alter the method names, and indeed change method implementation where desirable (as you might notice I did with the fireActionPerformed method, taking a Scala ActionEvent rather than a Java one). I don’t really expect these precise challenges with the constructors to come up often, but it was an instructive exercise. Oh, and here’s the ColorPanel example updated with the new Timer:

import swing._
import java.awt.Color

object ColorPanel extends SimpleSwingApplication {

  def top = new MainFrame {
    title = "Flash!"
    contents = p
  }

  val timer = new ScalaTimer(100)

  val p = new Panel {
    var c: Color = new Color(0)
    preferredSize = new Dimension(200, 200)
    listenTo(timer)
    reactions += {
      case e: TimerEvent => {
        c = new Color((c.getRGB + 1000) % 16777216)
        repaint
      }
    }

    override def paintComponent(g: Graphics2D) {
      g.setColor(c)
      g.fillRect(0, 0, size.width, size.height)
    }
  }

  timer.start()
}

1 Comment on Composition vs Inheritance: Converting Java’s Swing Timer class to Scala

  1. Kevin says:

    Thanks, this was a useful read. (I came here from link you placed on a StackOverflow page a couple of years back)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>