views:

213

answers:

1

Why do the default values here behave differently when assigned explicitly to a val, versus printed directly?

package blevins.example

class SimpleWrap[T] {
  var t: T = _
  def get = t
}

object App extends Application {
  val swb = new SimpleWrap[Boolean]
  val b = swb.get
  println("b: " + b)  // b: false
  println("swb.get: " + swb.get) // swb.get: null

  val swi = new SimpleWrap[Int]
  val i = swi.get
  println("i: " + i) // i: 0
  println("swi.get: " + swi.get) // swi.get: null
}

I'm using 2.8r19890.


Edit - It seems the strangeness happens when "get" is called expecting an Any.

  val any1: Any = swb.get
  val any2: Any = b
  println("any1: " + any1) // any1: null
  println("any2: " + any2) // any2: false
+4  A: 

I'm pretty sure that this is something to do with boxing/unboxing of the primitives. If you write generic code to work on primitives, you have to box the primitive and then unbox it at the place you've used it as a primitive. I am not sure what unboxing algorithm is used, but I suppose it is along the following lines:

if(box == null) 
  default value
else
  box.unbox

Therefore, very strangely I might add, the default value of the field t in your simple wrapper class is always going to be null, as the field is always going to be a boxed primitive, as generics are implemented at the JVM level by type erasure. Therefore, all the JVM sees is that t is of type Object, with a value of null. The method get will therefore always return null, but when the generic method get is supposed to return a primitive type, the null gets unboxed to the default value.

Also, a bit of poking around with reflection does indeed show that the field is indeed null.

val sw = new SimpleWrap[Boolean]
sw.getClass.getDeclaredFields.map {
  f => f.setAccessible(true)
  f.get(sw) 
  }

Oh the fun of nulls. One solution to this problem would be to use the 2.8 @specialised annotation, if that has been implemented in the nightly build you use.

Or, even better, the Scala compiler could default those fields to boxed defaults of the actual defaults of the primitives used. For example, in the case of a SimpleWrap[Boolean], t would have the type Object and the value java.lang.Boolean(false) at runtime.

EDIT: Bug report submitted.

Another weird thing:

val x: Int = null.asInstanceOf[Int] // 0
val y: Boolean = null.asInstanceOf[Boolean] // false

This is something that should be solved in order for generics to really be generic, and have consistent behaviour! At the moment your get method doesn't have a consistent behaviour.

-- Flaviu Cipcigan

Flaviu Cipcigan
Thanks for the investigation!
Mitch Blevins
You're welcome :). It was definitely an interesting problem to look at.
Flaviu Cipcigan