Scala - метод, не распознаваемый в конструкторе - PullRequest
2 голосов
/ 27 июля 2011

Я пытаюсь расширить javax.swing.Timer, но у него есть только один конструктор:

Timer(int delay, ActionListener listener) 

Я не хочу, чтобы мой подкласс в Scala брал Java ActionListenerконструктор.Я прочитал в очень старом потоке , что «нет способа напрямую вызвать конструктор суперкласса; вы должны пройти мимо основного конструктора вашего собственного класса», так что похоже, что я застрял сActionListener в первичном конструкторе.Поэтому я добавил вспомогательный конструктор следующим образом:

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
      }
    })
//    publish(TimerEvent(outer)) // <-- publish recognized here
  }
}

Однако я получаю ошибку компиляции error: not found: value publish ... почему?И как исправить?

Ответы [ 2 ]

5 голосов
/ 27 июля 2011

Здесь на самом деле есть две проблемы: и publish, и outer не будут доступны, пока конструктор не завершит работу. Кажется, в Scala есть правило, что на экземпляр this нельзя ссылаться во вспомогательном конструкторе, пока основной конструктор не завершит работу. Например, следующее не удастся скомпилировать:

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

Вместо вспомогательного конструктора, как насчет определения фабричного метода для объекта-компаньона?

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
  }
}

Обратите внимание, что прямое указание ret требует использования lazy val вместо val. Предположительно actionPerformed не вызывается, пока не вернется конструктор, иначе ret будет недействительным из-за бесконечной рекурсии.

1 голос
/ 27 июля 2011

Вот обходной путь, который я нашел в данном конкретном случае: отправьте фиктивный ActionListener, затем удалите и замените реальным.

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))
    }
  })
}

Редактирование: еще один прием: создание основного конструктора private, чтобынет возможности ошибочно попробовать конструировать с вашим собственным ActionListener.

Edit 2: или вообще избежать вспомогательного конструктора, передав анонимный ActionListener в подпись.

Edit 3 - решено!: Я только что прочитал в Javadoc, что ActionListener, переданный конструктору, может быть нулевым!Таким образом, все, что нам действительно нужно, это:

class ScalaTimer2 (delay: Int) extends Timer(delay, null) with Publisher {
  outer =>
  addActionListener(new ActionListener {
    def actionPerformed(e: ActionEvent) {
      publish(new TimerEvent(outer))
    }
  })
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...