1

I have a piece of code that relies on the existence of an arbitrary element of a certain case class in order to operate on the class's fields. There are some options out there, and even though pretty much every Scala blog recommends not using null ever, it seems like not a terrible idea in type level programming (e.g. this answer: https://stackoverflow.com/a/4443972/1650448). However, this code directly casting null to a particular case class does not work and does not throw an error, and I'm interested in why.

trait V {}
case class W(x: Int) extends V

val y = null.asInstanceOf[W]

y match {
  case w:W => println("null cast to W is a W")
  case _ => println("null cast to W is NOT a W")
}

// prints "null cast to W is NOT a W"

y match {
  case v:V => println("null cast to W is a V")
  case _ => println("null cast to W is NOT a V")
}

// prints "null cast to W is NOT a V"

val z = W(1)

z match {
  case w:W => println("instantiated W is a W")
  case _ =>  println("instantiated W is NOT a W")
  }

// prints "instantiated W is a W"

z match {
  case v:V =>  println("instantiated W is a V")
  case _ =>  println("instantiated W is NOT a V")
}

// prints "instantiated W is a V"

Michael K
  • 2,196
  • 6
  • 34
  • 52
  • 2
    This is not type level programming and therefore the advice to avoid `null` stands. Use `Option`. – Tim Nov 12 '19 at 09:10

2 Answers2

3

Casting changes the compile-time type, not the runtime type. Pattern matching checks the runtime type. In fact, case class pattern matching even has an explicit check for null; see Why is there a `null` check in the implementation of the `unapply` method for case classes? (though that check doesn't affect your case since you're using a type pattern).

Also, even if it weren't for the pattern matching issue, you wouldn't be able to "operate on the class's fields" without getting a NullPointerException.

Brian McCutchon
  • 8,354
  • 3
  • 33
  • 45
2

Because Type Patterns are defined so:

  • A reference to a class C, p.C, or T#C. This type pattern matches any non-null instance of the given class.

So: null is a perfectly valid value of type W; but it won't be matched by the pattern w: W.

And the main reason why it isn't matched is precisely

I have a piece of code that relies on the existence of an arbitrary element of a certain case class in order to operate on the class's fields.

so when you match against w: W you want to know its fields and methods are available. But for null they aren't.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487