开发者

Scala - method not recognized in constructor

I'm trying to extend javax.swing.Timer, but it only has one constructor, which is

Timer(int delay, ActionListener listener) 

I do not want my subclass in Scala to take a Java ActionListener in its constructor. I read in a very old thread that "there is no way to call a superclass constructor directly; you have to pass by the primary constructor of your own class", so it looks like I'm stuck with the ActionListener in the primary constructor. So I've added an auxiliary constructor thus:

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

class ScalaTimer2 (delay: Int, listener: java.awt.event.ActionListener)
      extends javax.swing.Timer(delay, listener) with swing.Publisher {
  outer =>
  def this(delay: Int) = {
    this(delay, new java.awt.event.ActionListener {
      def actionPerformed(e: java.awt.event.ActionEvent) {
        publish(TimerEvent(outer))   // <-- publish not recogonized
开发者_JS百科      }
    })
//    publish(TimerEvent(outer)) // <-- publish recognized here
  }
}

However I get a compile error error: not found: value publish ... why? And how to fix?


There are actually two problems here: both publish and outer will not be available until the constructor has completed. Scala seems to have a rule that this instance cannot be referenced in an auxiliary constructor until the primary constructor has completed. For example, the following will fail to compile:

class Foo (x: Unit) { def this() { this(println(this)) } }

Rather than an auxiliary constructor, how about defining a factory method on the companion object?

object ScalaTimer2 {
  def apply(delay: Int): ScalaTimer2 = {
    lazy val ret: ScalaTimer2 = new ScalaTimer2(delay, new java.awt.event.ActionListener {
      def actionPerformed(e: java.awt.event.ActionEvent) {
        ret.publish(TimerEvent(ret))
      }
    })
    ret
  }
}

Note that the forward reference of ret requires the use of a lazy val instead of just val. Presumably actionPerformed is not called until the constructor returns, otherwise ret will be invalid due to infinite recursion.


Here's a workaround I found in this particular case: send a dummy ActionListener then remove and replace with the real one.

class ScalaTimer2 private (delay: Int, listener: java.awt.event.ActionListener)
      extends javax.swing.Timer(delay, listener) with swing.Publisher {
  outer =>

  def this(delay: Int) = 
    this(delay, new java.awt.event.ActionListener {
      def actionPerformed(e: java.awt.event.ActionEvent) { } // dummy
    })
  removeActionListener(listener)

  addActionListener(new java.awt.event.ActionListener {
    def actionPerformed(e: java.awt.event.ActionEvent) {
      publish(new TimerEvent(outer))
    }
  })
}

Edit: another trick: making the primary constructor private so that there's no opportunity to mistakenly try constructing with your own ActionListener.

Edit 2: or avoid the auxiliary constructor altogether by passing an anonymous ActionListener in the signature.

Edit 3 - solved! : I just read in the Javadoc that the ActionListener passed to the constructor can be null! So all we actually need is:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜