Scala类成员和构造函数参数名称冲突

时间:2010-09-22 01:01:59

标签: scala properties constructor field

考虑以下用Java编写的类:

class NonNegativeDouble {
    private final double value;
    public NonNegativeDouble(double value) {
        this.value = Math.abs(value);
    }
    public double getValue() { return value; }
}

它定义了一个名为value的最终字段,该字段在构造函数中初始化,方法是将其参数称为alike并对其应用函数。

我想在Scala中编写与它类似的东西。起初,我尝试过:

class NonNegativeDouble(value: Double) {
  def value = Math.abs(value)
}

但编译器抱怨:错误:重载方法值需要结果类型

显然编译器认为表达式value中的表达式Math.abs(value)是指定义的方法。因此,定义的方法是递归的,所以我需要声明它的返回类型。因此,我编写的代码没有按照我的预期执行:我希望value内的Math.abs(value)引用构造函数参数value,而不是指定义的方法。就好像编译器隐式地将this.添加到Math.abs(this.value)

向构造函数参数添加valvar(或private ...变体)似乎没有帮助。

所以,我的问题是:我可以定义一个与构造函数参数同名的属性,但可能是一个不同的值?如果是这样,怎么样?如果没有,为什么?

谢谢!

4 个答案:

答案 0 :(得分:17)

不,你不能。在Scala中,构造函数参数属性,因此重新定义它们是没有意义的。

解决方案当然是使用另一个名称:

class NonNegativeDouble(initValue: Double) {
  val value = Math.abs(initValue)
}

像这样使用,initValue将不是创建的实例的一部分。但是,如果您在def或模式匹配声明中使用它,那么它将成为该类的每个实例的一部分。

答案 1 :(得分:4)

@Daniel C. Sobral

class NonNegativeDouble(initValue: Double) {
  val value = Math.abs(initValue)
}

你的代码是正确的,但“构造函数参数是属性”,这不是真的。

A post from the official site said

  

如果是,则将类Foo(x:Int)等参数转换为字段   在一个或多个方法中引用

马丁的回复证实了其真相:

  

这一切都是正确的,但它应该被视为一种实现   技术。这就是规范对此保持沉默的原因。

通常情况下,我们仍然可以将主构造函数参数视为常规方法参数,但是当任何方法引用参数时,编译器会巧妙地将其转换为私有字段。

如果val前面有任何形式参数,编译器会自动生成一个getter定义。如果var,则另外生成一个setter。见语言准备部分5.3。

这就是主要的构造函数参数。

答案 2 :(得分:2)

您可以考虑参数字段

class NonNegativeDouble(val value: Double, private val name: String ){
  if (value < 0) throw new IllegalArgumentException("value cannot be negative")
  override def toString = 
    "NonNegativeDouble(value = %s, name = %s)" format (value, name)
}

val tom = "Tom"
val k = -2.3

val a = new NonNegativeDouble(k.abs, tom)
a: NonNegativeDouble = NonNegativeDouble(value = 2.3, name = Tom)

a.value
res13: Double = 2.3

a.name
<console>:12: error: value name in class NonNegativeDouble cannot be accessed in NonNegativeDouble
     a.name

val b = new NonNegativeDouble(k, tom)
java.lang.IllegalArgumentException: value cannot be negative
...

它定义了具有相同名称“value”,“name”的字段和参数。 您可以添加修饰符,例如private ...

答案 3 :(得分:0)

如果是case classes,则应该是:

case class NonNegativeDouble(private val initValue: Double) {
  val value = Math.abs(initValue)
  def copy(value: Double = this.value) = NonNegativeDouble(value)
}

需要copy的实现来防止绑定initValue参数的编译器的错误版本。

我希望编译器足够聪明,不能保留initValue的“额外空间”。我还没有验证这种行为。