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
== javaint
). 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:
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.Since you know you must be boxed, given the code you wrote, you may as well just check for
Integer
instead ofInt
(that is, useclassOf[Integer]
).