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
  }
}
1
  • What do you mean by "subj"? Commented Jun 13, 2014 at 6:44

2 Answers 2

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]
Sign up to request clarification or add additional context in comments.

2 Comments

In another comment you say that the cast is not required, but it is still in your code.
Whoops, my mistake. Edited to remove it.
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)

6 Comments

There should be a lower bound on T - trait THasArray[T >: Null] - and then you don't need the cast.
I don't think Null lower bounds would actually work. There was attempt to avoid null at type level, but now it seems abandoned.
The Null type is still around. It isn't an attempt to avoid null; on the contrary, it brings null into the type system.
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 without lazy it would result in NPE if you forgot to early initialize fields used by val: new HasArray[Int] { val ev = implicitly[ClassTag[Int]]; val size = 4 }
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.