4

I find I occasionally want to write an arbitrary assertion in a trait, or in some other place where the thing I want to make an assertion on is not yet fully defined.

trait Foo {
  val aList: List[String]
  val anotherList: List[String]

  def printPairs = aList.zip(anotherList).foreach(println)

  assert(aList.size == anotherList.size) // NullPointerException, because these references are undefined right now.
}

I suppose a generalization of what I'm looking for is a hook that (always) fires after the class is fully defined and instantiated, since this is the sort of check I'd generally put in a constructor.

randomstatistic
  • 800
  • 5
  • 11

1 Answers1

1

You can do it using Early Definitions (search for it in the Scala Language Reference for more info) - with your trait exactly as you wrote it, you can extend it as follows:

class Bar(l1: List[String], l2: List[String]) extends {
  val aList = l1
  val anotherList = l2
} with Foo

Which would initiate the lists before calling the assert, therefore:

new Bar(List("a", "b"), List("c", "d")) // built successfully
new Bar(List("a", "b"), List("c", "d", "e")) // throws exception

Of course - it's not exactly what you asked for (a "hook" that's always called after construction), as any extending class would have to know which members must be overridden "early", but as far as I know that's the closest you can get.

Tzach Zohar
  • 37,442
  • 3
  • 79
  • 85
  • I think seven-minutes-until-answered is a record for me. You're right that it's not exactly what I had in mind, but it's pretty close. The extending class must do the Early Definition clause, or the assertion will fail, and the clause must pass the assertion, or the assertion will fail. Either way, only a class that survives the assertion can be instantiated. – randomstatistic Nov 19 '16 at 00:31
  • For how-to-do-this-in-Scala questions you can see 2-minute answers quite often, there are quite a few reputation hunters out there, you snooze you loose ;) Glad it helps! – Tzach Zohar Nov 19 '16 at 00:36
  • 3
    `class Bar(val aList: List[String], val anotherList: List[String]) extends Foo` (a slightly cleaner version) – jwvh Nov 19 '16 at 00:49
  • I also ran into https://stackoverflow.com/questions/27494290/anonymous-partial-function-in-early-initializer-requires-premature-access-to-cl, because one of the trait-vals I wanted was type PartialFunction. – randomstatistic Nov 28 '16 at 18:14