I am designing the interface for a system that can accept rules to be applied to a given data structures.
The main system is supposed to work as a driver receiving commands like "apply rule X to arguments U,V,W,...". I don't know all possible rules at compile time, so I thought to embed the argument type information on the rule definition and verify it latter.
Right now the rule definition is as follows:
trait Rule {
val argTypes: Seq[Class[_]]
def apply(stt: State, args: Seq[Any])
}
The number of actual arguments in args
must match the number of type defined in argTypes
, and the method apply
should return an manipulate the state. (Actually, this is simplified explanation but this is the general idea).
I also implemented a function called checkTypes
to verify if the types of the actual arguments match the type defined in argTypes
.
def checkTypes(args: Seq[Any]) {
if (argTypes.size != args.size) {
val msg = "Number of arguments (%d) does not match expected number (%d)."
throw new IllegalArgumentException(msg.format(args.size, argTypes.size))
}
val err = "Incompatible argument type for [%s]. Expected: %s. Found: %s."
for (i <- 0 until argTypes.size) {
val formalClass = argTypes(i)
val arg = args(i)
val actualClass = arg.asInstanceOf[AnyRef].getClass
if (!(formalClass isAssignableFrom actualClass)) {
val errMsg = err.format(arg, formalClass.getName, actualClass.getName)
throw new IllegalArgumentException(errMsg)
}
}
}
The problem is that whenever I try to pass integer arguments (read from console or text file) the checkTypes
procedure fails with this message: java.lang.IllegalArgumentException: Incompatible argument type for [1]. Expected: int. Found: java.lang.Integer.
I am converting the integer arguments with Integer.parseInt(t).asInstanceOf[Int]
and the rule expects two arguments of type Int
So, are there a more effective way to check for type of arguments at runtime?
OR
How do I convert String
to Int
for real?
Thanks in advance.
As a minimum working example, this is a session in the Scala REPL that rises the exception:
scala> val argTypes: Seq[Class[_]] = Seq(classOf[Int], classOf[Int])
argTypes: Seq[Class[_]] = List(int, int)
scala> def checkTypes(args: Seq[Any]) {
| if (argTypes.size != args.size) {
| val msg = "Number of arguments (%d) does not match expected number (%d)."
| throw new IllegalArgumentException(msg.format(args.size, argTypes.size))
| }
| val err = "Incompatible argument type for [%s]. Expected: %s. Found: %s."
| for (i <- 0 until argTypes.size) {
| val formalClass = argTypes(i)
| val arg = args(i)
| val actualClass = arg.asInstanceOf[AnyRef].getClass
| if (!(formalClass isAssignableFrom actualClass)) {
| val errMsg = err.format(arg, formalClass.getName, actualClass.getName)
| throw new IllegalArgumentException(errMsg)
| }
| }
| }
checkTypes: (args: Seq[Any])Unit
scala> val args: Seq[Any] = Seq("1".toInt, "2".toInt)
args: Seq[Any] = List(1, 2)
scala> checkTypes(args)
java.lang.IllegalArgumentException: Incompatible argument type for [1]. Expected: int. Found: java.lang.Integer.
at $anonfun$checkTypes$1.apply$mcVI$sp(<console>:20)
at scala.collection.immutable.Range.foreach$mVc$sp(Range.scala:78)
at .checkTypes(<console>:14)
at .<init>(<console>:11)
at .<clinit>(<console>)
at .<init>(<console>:11)
at .<clinit>(<console>)
at $print(<console>)
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:616)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704)
at scala.tools.nsc.interpreter.IMain$Request$$anonfun$14.apply(IMain.scala:920)
at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43)
at scala.tools.nsc.io.package$$anon$2.run(package.scala:25)
at java.lang.Thread.run(Thread.java:679)
You have a mismatch between the boxed type (java.lang.Integer
) and the unboxed type (scala.Int
== java int
). The actual instance is boxed, but you're testing it against the primitive classOf.
Here's an example of what happens when primitives are boxed:
scala> 5.getClass
res0: Class[Int] = int
scala> (5: Any)
res1: Any = 5
scala> res1.getClass
res2: Class[_] = class java.lang.Integer
Note that if you pattern match on Any
and get a primitive, it will actually pick out the boxed copy and unbox it for you.
scala> res1 match { case i: Int => println(i); case _ => }
5
Since you know you must be boxed, given the code you wrote, you may as well just check for Integer
instead of Int
(that is, use classOf[Integer]
).