Нужна помощь с переменными экземпляра Scala - PullRequest
5 голосов
/ 16 декабря 2009

Примите этот код Java:

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

   private int x = 0;
}

Если я создам экземпляр A, например:

A a = new A("geo");

после этого вызова значение x будет равно 3. Что я делаю неправильно в моем коде Scala?

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

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

Это печатает 0. Я предполагал, что когда компилятор достигает var x:Int = 0, тело основного конструктора заканчивается. Я ошибся? Как еще можно объявить переменные экземпляра в Scala (если я не хочу, чтобы они были в моем конструкторе)?

Ответы [ 5 ]

7 голосов
/ 17 декабря 2009

Ваша путаница возникает из-за неправильного понимания того, как работают конструкторы в Scala. В частности, давайте переведем код Scala, который вы разместили, в Java:

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

становится

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

Причина проста. Все тело класса в Scala является основным конструктором этого класса. Это означает, что операторы в нем и инициализация val и var являются операторами, будут выполняться в том порядке, в котором они найдены.

PS: Вот фактическое, истинное исполнение этого кода.

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;
      ()
    }
  }
}
7 голосов
/ 17 декабря 2009

Имейте в виду, что ваш код переводится во что-то похожее (но не совсем) на это:

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;
  }
}

Но переменные, определенные в (неконструктивных) методах, переводятся в фактические локальные переменные.

Не уверен, что это объясняет рассуждения так сильно, как подчеркивает одно из различий.


РЕДАКТИРОВАТЬ - Изменен «перевод» с scala на java для ясности и способности более точно представлять происходящее.

4 голосов
/ 16 декабря 2009

var x: Int = 0 сделать это первой строкой конструктора

class A(val g:String) {  
   var x:Int = 0  
   x += g.length  
}
4 голосов
/ 16 декабря 2009

Изменить это:

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

до

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

}
0 голосов
/ 18 декабря 2009

Почему Scala позволяет вам ссылаться на x до того, как вы это объявите? В любой другой области это было бы незаконно.

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}
...