6

I am a Scala newbie. I've ploughed through a couple of books, and read some online tutorials. My first project is having problems, so I have reduced the code to the simplest thing that can go wrong.

I've searched google and stack overflow for scala/constructors/varargs, and read a couple of tours of scala.

The (nearly) simplest code is:

class Foo(val params: Int*)
case class Foo1(val p: Int) extends Foo(p)
case class Foo2(val p1: Int, val p2: Int) extends Foo(p1, p2)

object Demo extends App {
  override def main(args: Array[String]) {
    val f = Foo2(1, 2)
    f.p1
  }
}

The exception occurs when accessing p1 and is

Exception in thread "main" java.lang.ClassCastException: scala.collection.mutable.WrappedArray$ofInt cannot be cast to java.lang.Integer

Resorting to debugging using eclipse, I found an interesting property: When looking at variables

f   Foo2  (id=23)   
    p2  2   
    params  WrappedArray$ofInt  (id=33) 
        array    (id=81)    
            [0] 1   
            [1] 2   

So what happened to p1?

I'm sorry for troubling you with a newbie question

Stave Escura
  • 2,058
  • 1
  • 19
  • 24

3 Answers3

6

You are not wrong but the compiler is. It tries to get p1 out of p (in your case it would try to get Foo2.p1 out of Foo.params):

def p1(): Int = scala.Int.unbox(Main$$anon$1$B.super.p());

which is obviously a bug because it can't work. Instead it should assign p1 in the ctor of the subclass.

I reported a bug: SI-7436.

kiritsuku
  • 52,967
  • 18
  • 114
  • 136
  • Woot. My first Scala bug. OK thanks. I have a work around, but I appreciate the help. This reminded me of Fortran equivalence blocks, or Pascal variant records. – Stave Escura Apr 30 '13 at 12:18
  • Btw, `class X(val p: Int*)` doesn't make much sense. If you want to have access to `p` after the construction of X just use a sequence instead of varargs: `class X(val p: Seq[Int])` or `class X(p: Int*) { def params: Seq[Int] = p }`. – kiritsuku Apr 30 '13 at 12:22
1

I cannot explain to you /why/ Scala gets confused, but the following works:

class Foo(p: Int, ps: Int*)
case class Foo1(p1: Int) extends Foo(p1)
case class Foo2(p1: Int, p2: Int) extends Foo(p1, p2)

object Main extends App {
  println( Foo1(1) )
  println( Foo2(2, 3) )
}

Also note that, when extending App, you wouldn't want to override main.

fotNelton
  • 3,844
  • 2
  • 24
  • 35
  • I appreciate the comment on App. I am a newbie, so I just used the Hello World example in Eclipse. Thanks for the working code. I have something using lists that also works: my problem is one of understanding. Unfortunately I am very bothered about the "readability" of the code, and the intent of the variable params is to hold all the params. I'm wondering if this is a scala bug – Stave Escura Apr 30 '13 at 08:21
  • Having read your comment I think I should have mentioned that the above code also changes the semantics of your example because `Foo`'s constructor now requires at least one argument. Probably no biggy but that depends on the problem domain :) – fotNelton Apr 30 '13 at 09:45
0

You should have a glance at this comment and the answer just above it, I think it should answer your issue ;-)

Community
  • 1
  • 1
itsu
  • 222
  • 1
  • 10
  • Thanks for the response. I saw this before I posted the question, but I don't think it is the correct answer. In that question the person wanted to pass a Any* params to the constructor. I don't want to do that. In the case of Foo2 I am passing (p1,p2) to a Any*, and for some bizarre reason it is loosing access to variable p1, and replacing it with a call to get params, then having a class cast. Note that in the question the person couldn't get the code to compile. Mine compiles and runs – Stave Escura Apr 30 '13 at 08:45