I'm currently trying to figure out the depths of Scala's type system and Scala reflection.
I have the following example code (my real code is more complex and makes more sense, but boils down to this):
abstract class Node
class Foo extends Node
case class ArrayFoo(var subs : Array[Foo]) extends Foo
case class IntFoo(i : Integer) extends Foo
object Main {
def DoSomething(node : AnyRef) : AnyRef = { // Fix this function?
// do some stuff
node
}
def SetArrayWithReflection(n : Node, a : AnyRef) = {
if (a.isInstanceOf[Array[AnyRef]]) {
val arrayA = a.asInstanceOf[Array[AnyRef]].map(f => DoSomething(f)) // FIX this call?
val getter = n.getClass.getMethods.find(p => p.getName == "subs")
val setter = n.getClass.getMethods.find(p => p.getName == "subs_$eq")
if (setter == None) println("Method not found!") else {
println(f"would need to downcast from ${arrayA.getClass.getComponentType} to ${getter.get.getReturnType.getComponentType}")
setter.get.invoke(n, arrayA) // Error happens here
}
}
}
def main(args : Array[String]) : Unit = {
val my = ArrayFoo(Array(IntFoo(1), IntFoo(2), IntFoo(3)))
val newArray = Array(IntFoo(10), IntFoo(20), IntFoo(30))
SetArrayWithReflection(my, newArray)
my.subs.foreach(f => println(f))
}
}
The output I'm getting is:
would need to downcast from class java.lang.Object to class Foo
Exception in thread "main" java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at Main$.SetArrayWithReflection(Main.scala:19)
at Main$.main(Main.scala:27)
at Main.main(Main.scala)
Via reflection, I try to call a method expecting an Array[Object] with an Array[Foo], which obviously has to fail.
My question is: How can I downcast or create a suitable Array for method invocation?
The same code does work for Lists, however. I suppose this is due to type erasure which does not happen on Arrays.
Thanks in advance,
Velines
You can't downcast an Array[Object] to Array[Foo]. The simplest way to create the array you need is
val arrayFoo = Array.ofDim[Foo](arrayA.length)
System.arraycopy(arrayA, 0, arrayFoo, 0, arrayA.length)
See System.arraycopy.
Related
I have a helper class which creates instance of another class
class TestEnv {
val questionsController = new QuestionsController(...)
}
I am unit testing QuestionsController and have created a basic test case
class QuestionsControllerUnitSpec extends PlaySpec with BeforeAndAfterAll with BeforeAndAfterEach with OneAppPerSuiteWithComponents{
override def beforeEach() = {
println("------------new test -----------------")
}
override def components: BuiltInComponents = new BuiltInComponentsFromContext(context) with NoHttpFiltersComponents {
import play.api.mvc.Results
import play.api.routing.Router
import play.api.routing.sird._
lazy val router: Router = Router.from({
case GET(p"/") => defaultActionBuilder {
Results.Ok("success!")
}
})
}
"Question Controller " should {
"be created" in {
val testEnv = new TestEnv(components = components)
val qc:QuestionsController = testEnv.questionsController
qc mustBe defined //I get compilation error
}
}
}
I get the following compilation error
Error:(52, 10) could not find implicit value for parameter definition: org.scalatest.enablers.Definition[controllers.QuestionsController]
qc mustBe defined
Error:(52, 10) not enough arguments for method mustBe: (implicit definition: org.scalatest.enablers.Definition[controllers.QuestionsController])org.scalatest.Assertion.
Unspecified value parameter definition.
qc mustBe defined
I checked the definition of mustBe in MustMatchers.class. It is defined as def mustBe(right : org.scalatest.words.DefinedWord)(implicit definition : org.scalatest.enablers.Definition[T]) : org.scalatest.Assertion = { /* compiled code */ }
Why am I getting the error.
defined matcher syntax can be used with user defined types if we provide implicit implementation of Definition trait. For example, say we have a user defined class
class Foo {
val bar = 3
}
and we provide implicit definition
implicit val fooDefinition = new Definition[Foo] {
override def isDefined(foo: Foo): Boolean = foo.bar != null
}
then we can use defined syntax
(new Foo()) mustBe defined
If similar implicit implementation of Definition[QuestionsController] is provided, then the compiler error should be resolved.
I am happy to accept a different answer if it can provide more accurate answer. I suppose I am testing the wrong thing. What I am doing is similar to declaring an integer and checking if the integer exists! Instead I should be checking the value of the integer.
About matchers, more information is at http://doc.scalatest.org/3.0.1/#org.scalatest.MustMatchers. More information on Definition is on http://doc.scalatest.org/3.0.1/#org.scalatest.enablers.Definition
I want to implement stack data structure using kotlin. I want to use generic array so as to create stack of any datatype. I am not sure how to initialize the array properly. It shows different kind of errors everytime. Also cannot figure out how to use List<T>. Any kind of help will be appreciated.
class StackADT<ANY>(var capacity: Int) {
private var top = -1
private val stack: (generic type array)//NEED TO INITIALIZE PROPERLY HERE
fun push(element: ANY) {
if (top == capacity)
throw Exception("Overflow occurred in stack!!")
stack[++top] = element
}
....
class StackADT<T>(var capacity: Int) {
private var top = -1
private val stack: ArrayList<T> = ArrayList(capacity)
fun push(element: T) {
if (top == capacity)
throw Exception("Overflow occurred in stack!!")
top++
stack.add(element)
}
...
You can test here: Kotlin Playground
Another way:
var stack = arrayOfNulls<Any?>(capacity) as Array<T>
Just got this strange error when trying to write a stack with generic type in latest playground.
I really don't understand what's wrong here, can someone explain to me why I got this error?
class MyStack<T> {
var stack1 = [T]()
func push<T>(value: T) {
stack1.append(value) // Error: cannot invoke 'append' with an argument list of type '(T)'
}
}
The class is already generic, no need to make push generic too
class MyStack<T> {
var stack1 = [T]()
func push(value: T) {
stack1.append(value)
}
}
When push is declared as push<T>, the generic parameter overrides the one defined on the class. So, if we were to rename the generic parameters, we'd get
class MyStack<T1> {
var stack1 = [T1]()
func push<T2>(value: T2) {
stack1.append(value) // Error: cannot invoke 'append' with an argument list of type '(T2)'
}
}
presented like this, it makes sense that we cannot push a T2 in [T1].
From array.scala of scala-2.10.4, The Array is defined as
final class Array[T](_length: Int) extends java.io.Serializable with java.lang.Cloneable {
/** The length of the array */
def length: Int = throw new Error()
def apply(i: Int): T = throw new Error()
def update(i: Int, x: T) { throw new Error() }
override def clone(): Array[T] = throw new Error()
}
Please note, the apply method will throw an exception! And for the accompany object Arrry, I find the following codes:
def apply[T: ClassTag](xs: T*): Array[T] = {
val array = new Array[T](xs.length)
var i = 0
for (x <- xs.iterator) { array(i) = x; i += 1 }
array
}
I know there is an implicit parameter which is ClassTag[T], what make me surprised is how
new Array[T] (xs.length)
is compiled. By decompiling the Array.class, I find that line is translated to :
public <T> Object apply(Seq<T> xs, ClassTag<T> evidence$2)
{
// evidence$2 is implicit parameter
Object array = evidence$2.newArray(xs.length());
...
}
I am really confused by this kind of translation, what is the rule under the hood?
Thanks
Chang
The Scala Array Class is just a fake wrapper for the runtime so you can use arrays in Scala. You're probably confused because those methods on the Array class throw exceptions. The reason they did this is so that if you actually end up using the fake class it blows up since really it should be using the java runtime array, which does not have a proper container class like Scala. You can see how the compiler handles it here. When your using arrays in Scala you're probably also using some implicits from predef like ArrayOps and WrappedArray for extra helper methods.
TLDR: Scala compiler magic makes arrays work with the java runtime under the hood.
On the JVM arrays are exempt from type-erasure, e.g. at runtime instead of Array[_] there is a difference between Array[Int], Array[String] and Array[AnyRef] for example. Unlike Java, Scala can handle this mostly transparently, so
class Foo {
val foo = new Array[Int](123)
}
has a direct byte-code invocation for creating the integer array, whereas
class Bar[A](implicit ev: reflect.ClassTag[A]) {
val bar = new Array[A](123)
}
is solved by using the implicit type evidence parameter of type ClassTag[A] so that at runtime the JVM can still create the correct array. This is translated into the call you saw, ev.newArray(123).
I have defined a class in Scala (2.9.1) as follows:
class A(val neighbors: Array[Option[A]]) {
def this() = this(new Array[Option[A]](6))
// class code here ...
}
My problem is that neighbors is initialized with nulls, when I would like it to be initialized with None. I tried this, but the compiler complains with the error message "not found: type None":
class A(val neighbors: Array[Option[A]]) {
def this() = this(new Array[None](6))
// class code here ...
}
I can do this, which gives the desired behavior, but it doesn't seem very elegant:
class A(val neighbors: Array[Option[A]]) {
def this() = this(Array(None, None, None, None, None, None))
// class code here ...
}
So, my question is, what is the best way to do this?
EDIT: I am referring to the behavior when new A() is called.
The easiest way to do this would be
Array.fill(6)(None:Option[A])
Additionally you can change your class's constructor to take a default parameter like this:
class A(val neighbors: Array[Option[A]] = Array.fill(6)(None))
Maybe like this?
def this() = this(Array.fill(6) {Option.empty})