3

Is it possible to construct subj? Something like:

trait THasArray[T]
{
  val ARRAY_SIZE = 8
  val array = Array.fill[T](ARRAY_SIZE)(null)
}

doesn't work well - compiler complains about 'null', which I need to have. I know about Option, though the question is, whether this is possible with plain arrays.

Thanks.

EDIT:

Thanks both of you, guys. I've already found that trick with passing class tag as an implicit parameter.

Though, I've re-formulated my original issue a bit. I needed that array to be initialized only once, and never change. So here's my solution which doesn't require implicit val type tag, but instead uses init function to do the trick

trait THasArray[T >: Null]
{
  private var table: Seq[T] = null

  protected def init(elems: (Int, T)*)(implicit manifest: Manifest[T]) =
  {
    val size = (elems foldLeft 0)(_ max _._1)
    val array = Array.fill[T](size + 1)(null)
    elems foreach { x => array(x._1) = x._2 }
    table = array
  }
}
Target-san
  • 451
  • 3
  • 11

2 Answers2

2

First, method Array.fill requires implicit ClassTag in scope. Second, null has type Null, so you need to cast it to T. Third, array value should be lazy because ClassTag instance will be available only on HasArray creation.

trait HasArray[T] {
  implicit def ev: ClassTag[T]
  def size: Int
  lazy val array = Array.fill[T](size)(null.asInstanceOf[T]) 
}

case class HasStringArray(size: Int)(implicit val ev: ClassTag[String]) extends HasArray[String]

scala> HasStringArray(8).array
res11: Array[String] = Array(null, null, null, null, null, null, null, null)
4e6
  • 10,696
  • 4
  • 52
  • 62
  • There should be a lower bound on T - `trait THasArray[T >: Null]` - and then you don't need the cast. – Chris Martin Jun 13 '14 at 06:55
  • I don't think `Null` lower bounds would actually work. There was [attempt](http://www.scala-lang.org/api/current/index.html#scala.NotNull) to avoid null at type level, but now it seems abandoned. – 4e6 Jun 13 '14 at 07:08
  • The `Null` type is still around. It isn't an attempt to *avoid* null; on the contrary, it brings null into the type system. – Chris Martin Jun 13 '14 at 07:44
  • I have tried it without `lazy`, and it works as well, so why should the array being created lazily? As for the lower bound: If there is no lower bound, I managed to get an `HasArray[Int]`, and it works. Otherwise the lower bound prevents this. – Beryllium Jun 13 '14 at 10:01
  • @Beryllium without `lazy` it would result in NPE if you forgot to [early initialize](http://stackoverflow.com/questions/16348541/scala-example-use-for-early-definition-early-initializer-pre-initialized-fi) fields used by `val`: `new HasArray[Int] { val ev = implicitly[ClassTag[Int]]; val size = 4 }` – 4e6 Jun 13 '14 at 13:04
  • My apologies, I misunderstood intent of `Null` bounds. Another way to restrict `T` to reference types is by using `AnyRef` upper bounds. See [detailed explanation](http://stackoverflow.com/q/18054614/435904) and martin's [comment](https://groups.google.com/d/msg/scala-user/rfZpgGjfMyg/CRwH5T2Fd10J) on scala-user list. – 4e6 Jun 13 '14 at 13:25
2

T needs to have a lower bound of Null because not all values in Scala are nullable. The types that extends AnyVal are represented by JVM primitives which can't be null; for example, there's no such thing as a null Int.

Also, look at the method signature of Array.fill:

def fill[T: ClassTag](n: Int)(elem: => T): Array[T]

The context bound T: ClassTag means that fill has an implicit parameter of type ClassTag[T]. A trait's type parameter can't have a context bound, so unfortunately you have to go a bit out of your way to get the ClassTag into scope when you inherit THasArray.

trait THasArray[T >: Null] {
  implicit def classTagT: ClassTag[T]
  val ARRAY_SIZE = 8
  lazy val array = Array.fill[T](ARRAY_SIZE)(null)
}

class Foo[T >: Null](implicit val classTagT: ClassTag[T])
  extends THasArray[T]
Chris Martin
  • 30,334
  • 10
  • 78
  • 137