Scala:类参数访问与对象字段访问

时间:2017-03-09 14:52:06

标签: java scala class parameters field

我来自Java背景,是Scala的新手,目前正在阅读“Scala编程”一书。

书中有一个例子如下:

class Rational(n: Int, d: Int) { // This won't compile
  require(d != 0)
  override def toString = n + "/" + d

  def add(that: Rational): Rational = new Rational(n * that.d + that.n * d, d * that.d)
}

但是,鉴于此代码,编译器会抱怨:

error: value d is not a member of Rational
       new Rational(n * that.d + that.n * d, d * that.d)
                             ^
error: value n is not a member of Rational
       new Rational(n * that.d + that.n * d, d * that.d)
                                      ^
error: value d is not a member of Rational
       new Rational(n * that.d + that.n * d, d * that.d)
                                                      ^

解释说:

  

虽然类参数n和d在add方法的代码中,但是只能在调用add的对象上访问它们的值。因此,当您在添加的实现中说n或d时,编译器很乐意为您提供这些类参数的值。但是它不会让你说那个.n或那个。因为它没有引用调用了add的Rational对象。要访问分子和分母,您需要将它们放入字段中。

同样正确的实施如下:

class Rational(n: Int, d: Int) {
  require(d != 0)
  val numer: Int = n
  val denom: Int = d

  override def toString = numer + "/" + denom

  def add(that: Rational): Rational =
    new Rational(
      numer * that.denom + that.numer * denom,
      denom * that.denom
    )
}

我多次尝试理解这一点,但仍然不清楚。

我已经在课程级别拥有nd参数。我可以使用add方法访问它们。我正在将另一个Rational对象传递给add方法。它也应该是nd,对吧?

that.nthat.d有什么问题?为什么我需要在字段中获取参数?

此外,被覆盖的toString方法只是采用nd,这怎么会失败?

我可能听起来很愚蠢,但在我继续前进之前,确实需要清楚地理解这一点以获得更好的基本面。

3 个答案:

答案 0 :(得分:6)

传递给类构造函数的参数默认为私有成员,因此可用于所有类代码(如toString覆盖中所示),但它们不能作为实例成员访问(因此that.d不起作用。)

您可以告诉编译器不要使用默认值。

class Rational(val n: Int, val d: Int) { // now it compiles
  ...

或者,传递给case class的参数默认为实例成员。

case class Rational(n: Int, d: Int) { // this also compiles
  ...

答案 1 :(得分:2)

Scala具有比Java更多类型的访问修饰符。在Scala中有一个名为private[this]的东西,意味着"私有的当前对象"这比普通private更严格,这意味着"私有这个类的所有对象"。

class Rational(n: Int, d: Int)

基本相同
class Rational(private[this] val n: Int, private[this] val d: Int)

在更高,几乎是哲学层面你可以说

class Rational(n: Int, d: Int) { ... }

就像一个返回Rational的静态方法,它有参数nd,并且在任何方法中它的参数都是方法的本地参数。通过使用valvar对参数进行限定,您可以将这些参数转换为Rational的字段,而无需将它们写入两次:一次作为方法的参数(或构造函数,即这个静态方法的更具体的名称),以及一次作为字段。

答案 2 :(得分:0)

我正在读同一本书,我也有同样的疑问。当我反编译代码时,这就是生成的内容。

class Employee(var age: Int)

转译为

public class Employee {
   private int age;

   public int age() {
      return this.age;
   }

   public void age_$eq(final int x$1) {
      this.age = x$1;
   }

   public Employee(final int age) {
      this.age = age;
      super();
   }
}

接下来,

class Employee(val age: Int)

转译为

public class Employee {
   private final int age;

   public int age() {
      return this.age;
   }

   public Employee(final int age) {
      this.age = age;
   }
}

接下来,

class Employee(age: Int)

转译为

public class Employee {
   public Employee(final int age) {
   }
}
  • 似乎有一个Employee构造函数,只有一个名为age的参数。这清楚地表明 age 没有附加到 Employee 的实例。 Employee 上没有名为 age 的实例属性。
  • 但是,如果我们添加一个简单的年龄 getter,则会生成一个私有的最终属性。

示例

class Employee(age: Int) {
  def getAge(): Int = age
}

转译为

public class Employee {
   private final int age;

   public int getAge() {
      return this.age;
   }

   public Employee(final int age) {
      this.age = age;
   }
}
相关问题