views:

281

answers:

5

Assume this Java code:

public class A {
   public A(String g) {
      x += g.length();
   }

   private int x = 0;
}

If I create an instance of A, like this:

A a = new A("geo");

after this call, the value of x will be 3. What am I doing wrong in my Scala code?

class A(val g:String) {
  x += g.length
  var x:Int = 0
}

object x extends Application {
  val x = new A("geo")
  println(x.x)
}

This prints 0. I assumed that when the compiler reaches the var x:Int = 0, the body of the main constructor has ended. Am I wrong? How else could you declare instance variables in Scala ( assuming I don't want them in my constructor ) ?

+4  A: 

Change this:

class A(val g:String) {
  x += g.length
  var x:Int = 0
}

to

class A(val g:String) {
  var x = g.length

}
Jim Barrows
Why not just make it a `val`? There's no evidence from the sample code that it needs to be modified.
Alan Krueger
Yeah, that would be better
Jim Barrows
+4  A: 

var x:Int = 0 make this the first line of the constructor

class A(val g:String) {  
   var x:Int = 0  
   x += g.length  
}
+5  A: 

Keep in mind that your code translates into something similar (but not exactly) to this:

public class A {

  private final String g;
  private int x;

  public A(String g) {
    this.g = g;
    x_$eq(x() + g.length());
    x = 0;
  }

  public void x_$eq(int arg0) {
    x = arg0;
  }

  public int x() {
    return x;
  }

  public String g() {
    return g;
  }
}

But vars defined in (non-constructor) methods get translated into actual local variables.

Not sure if this explains the reasoning so much as highlights one of the differences.


EDIT - Changed "translation" from scala to java for clarity and ability to more accurately represent what is happening.

Mitch Blevins
So, why wasn't `_x` 3? That's how I'm reading the code representation.
Geo
But how is `x` treated? As a local variable? Is the compiler declaring it?
Geo
Edited to show the assignment to 0 for the private field.
Mitch Blevins
The var x is in instance variable in all cases. With the declaration var x: Int = 0, the variable x is a private variable with public x() and x_=() methods automatically created.
faran
+7  A: 

Your confusion results from a misunderstanding of how constructors in Scala works. Specifically, let's translate the Scala code you posted into Java:

class A(val g:String) {
  x += g.length
  var x:Int = 0
}

becomes

public class A {
   public A(String g) {
      x += g.length();
      x = 0;
   }
   private int x;
}

The reason is simple. The whole body of a class in Scala is the primary constructor for that class. That means the statements in it, and initializing val and var are statements, will be executed in the order they are found.

PS: Here is the actual, true rendition of that code.

Scala 2.7

C:\Users\Daniel\Documents\Scala\Programas> scalac -print A.scala
[[syntax trees at end of cleanup]]// Scala source: A.scala
package <empty> {
  class A extends java.lang.Object with ScalaObject {
    @remote def $tag(): Int = scala.ScalaObject$class.$tag(A.this);
    <paramaccessor> private[this] val g: java.lang.String = _;
    <stable> <accessor> <paramaccessor> def g(): java.lang.String = A.this.g;
    private[this] var x: Int = _;
    <accessor> def x(): Int = A.this.x;
    <accessor> def x_=(x$1: Int): Unit = A.this.x = x$1;
    def this(g: java.lang.String): A = {
      A.this.g = g;
      A.super.this();
      A.this.x_=(A.this.x().+(g.length()));
      A.this.x = 0;
      ()
    }
  }
}

Scala 2.8

C:\Users\Daniel\Documents\Scala\Programas>scalac -print A.scala
[[syntax trees at end of cleanup]]// Scala source: A.scala
package <empty> {
  class A extends java.lang.Object with ScalaObject {
    <paramaccessor> private[this] val g: java.lang.String = _;
    <stable> <accessor> <paramaccessor> def g(): java.lang.String = A.this.g;
    private[this] var x: Int = _;
    <accessor> def x(): Int = A.this.x;
    <accessor> def x_=(x$1: Int): Unit = A.this.x = x$1;
    def this(g: java.lang.String): A = {
      A.this.g = g;
      A.super.this();
      A.this.x_=(A.this.x().+(g.length()));
      A.this.x = 0;
      ()
    }
  }
}
Daniel
Good to see you have as much difficulty as me free-handing java without slipping in scala-isms. You missed a semi-colon and used the non-existent-in-java underscore initializer.
Mitch Blevins
I wish Java would give up the ghost already. :-)
Daniel
Ok, code fixed, and compiled to make sure. Why can't Java be reasonable? :-)
Daniel
Ok, I fixed the code, though I see you offered a superior answer (ie, a more accurate translation into Java).
Daniel
A: 

Why does Scala let you reference x before you declared it? In any other scope, this would be illegal.

scala> def foo(g:String) = { x+=1; var x=0; x}
<console>:4: error: forward reference extends over definition of variable x
       def foo(g:String) = { x+=1; var x=0; x}
Ken Bloom