Scala 2.10 reflection: creating an array from a type
I'm having trouble copying the Scala documentation (somewhat sparse?) In the new version 2.10. I have a situation where I am reading data recursively from a source without type information. While reading, I know the expected type, so I can check if that type matches the input.
My problem: When trying to get a collection object with type parameters (for example Array[Int]
), how can I use the expected type to make sure the read values are of the correct type?
So far I have searched for the code provided by the Scala Api that allows you to retrieve type parameters. I also read about Class Tags and how they can be used to create arrays. So my next thought was to 1) find the type parameter, 2) create an array from that type, and 3) see if the read data is not read without exception, like so:
val paramType = paramInfo[Array[X]] // Step 1: Find the X type parameter
val array = Array[paramType](size) // Step 2: Can't use TypeTags#Type as a normal Java type...
// Step 3: Feed data into the array and hope for the best
// Step 4: Profit!
Since the above paramType
gives me a Type, I just need to convert that type to a class tag. But the answer is deceiving me.
To be honest, this solution seems a little messy to me, but I couldn't figure out anything smarter. If there are alternative solutions, I'm all ears!
Thanks in advance.
Edit: To clarify, my above example should demonstrate that I want to extract type X from Array [Int] (for example) and then instantiate an array containing that particular type. Hope this is clearer.
Edit: Perhaps further clarification is in order (did I really make it obscure? :-)). I want to read a collection from a data source. And I want this collection to be printed with the correct, expected type. So let's say that the readData
. Since I know what type to expect, I give it a type parameter of that expected type. As an example, suppose Array [Int]. It can be Array [String] or Iterable [Any] or just Null or whatever. When the method is calledreadData
, I would like to map the given expected type (Array [Int]) to the data type read from an external source. If the type found is the same type - or a subtype of the expected type, we can display and return data. If not, an exception is thrown informing the user that the data found is not of the expected type. So, to summarize: how do I call for readData[Array[Int]]
work?
Edit: I solved the problem by creating Array [Any], extracting the expected type (X above) and iterating over the array to see if the elements are of the same type (or subtype) X. If there are, we can safely apply to array [X] ... In the example below, the expected type is represented by E. It's pretty hacky, I know, but again: I'd love to see alternatives ...
// Matches if the type of o corresponds (<:<) to the type of x
def verifyType(o : Any, x : Type) : Boolean = { ... }
// Get the type of the array parameter (X)
val tpe = reflect.runtime.universe.typeOf[E] tpe match {
// The returned Type is not an instance of universe.Type - hence the cast
case TypeRef(_, _, args) => args.asInstanceOf[List[Type]]
case _ => throw new IllegalArgumentException("Could not find type parameters in type " + tpe)
}
// Iterate the array and make sure the types match
val hasWrongType = array.exists(e => !verifyType(e, tpe))
if (hasWrongType) throw new Exception(...) // The types does not fit
else array.asInstanceOf[E] // We can safely cast
source to share
You really do not have anything new to Scala 2.10, but instead of an earlier version ClassManifest
you're using a replacement ClassTag
:
def makeArray[T : reflect.ClassTag](length: Int): Array[T] = {
val tTag = implicitly[reflect.ClassTag[T]]
tTag.newArray(length)
}
In the REPL:
scala> makeArray[String](5)
res0: Array[String] = Array(null, null, null, null, null)
With primitive types:
scala> makeArray[Int](5)
res1: Array[Int] = Array(0, 0, 0, 0, 0)
Edit : take two:
def makeArrayLike(a: Array[_], length: Int): Array[_] = {
val cA = a.getClass
val cC = cA.getComponentType
java.lang.reflect.Array.newInstance(cC, length).asInstanceOf[Array[_]]
}
In the REPL:
scala> val ai1 = Array[Int](1, 2, 3)
ai1: Array[Int] = Array(1, 2, 3)
scala> val as1 = Array[String]("one", "two", "three")
as1: Array[String] = Array(one, two, three)
scala> makeArrayLike(as1, 5)
res0: Array[_] = Array(null, null, null, null, null)
scala> makeArrayLike(ai1, 5)
res1: Array[_] = Array(0, 0, 0, 0, 0)
source to share