1

I'm trying to create a listener design Pattern like that:

abstract class Listener(g: Engine) {
  g.addListener(this)
}
class Listener1(g: Engine) extends Listener(g)
class Listener2(g: Engine) extends Listener(g)

class Engine {
  val listener1 = new Listener1(this)
  val listener2 = new Listener2(this)  

  var listeners: List[Listener] = Nil
  def addListener(g: Listener) = {
    listeners = g::listeners
  }
}

But if fails with a NullPointerException, because listeners is initially equal to null when listener1 and listener2 are created.

How do I overcome this problem?

EDIT: I tried the following:

  def addListener(g: Listener) = {
    if(listeners == null) {
      listeners = List(g)
    } else {
      listeners = g::listeners
    }
  }

But the problem is that after the class is initialized, listeners = Nil. I need a better design pattern to achieve my goal.

SOLUTION: Without using lazy val or mutable collections:

var listeners: List[Listener] = _
def addListener(g: Listener) = {
  if(listeners == null) 
    listeners = List(g)
  else
    listeners = g::listeners
}

Using _ for initialization instead of 'null' or 'Nil' makes the compiler not to overwrite listeners after listener1 and listener2 have been created.

Mikaël Mayer
  • 10,425
  • 6
  • 64
  • 101

1 Answers1

2

With a help of lazy value and mutable collection:

class Engine {
  val listener1 = new Listener1(this)
  val listener2 = new Listener2(this)  

  lazy val listeners = collection.mutable.ListBuffer.empty[Listener]
  def addListener(g: Listener) {
    listeners += g
  }
}

If you badly need immutable List somewhere down the road, just turn buffer into it with .toList method in the callplace.

Community
  • 1
  • 1
om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
  • Thank you, it works. But why? If I understand correctly, the lazy val defines a new class where the content is computed after the first call, isn't it ? But in my case, listeners might not even be defined in the method addListener, even if it is lazy. – Mikaël Mayer Apr 25 '13 at 12:37
  • unlike plain val, lazy val defers initialization to the moment of first access (and [has quite complicated implementation](http://stackoverflow.com/questions/3041253/whats-the-hidden-cost-of-scalas-lazy-val)) -- it is defined but just not set yet. Both lazy or not, listeners variable acts like a class field from addListener point of view. – om-nom-nom Apr 25 '13 at 12:46
  • I accept your solution because I like it, but I also added mine which works without lazy val nor mutable collections. – Mikaël Mayer Apr 25 '13 at 13:14