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

Scala类成员和构造函数参数名称冲突,scala,properties,constructor,field,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的最后一个字段,该字段在构造函数中初始化,方法是获取其

考虑以下用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)
}
但是编译器抱怨:错误:重载的方法值需要结果类型

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

val
var
(或
private…
变量)添加到构造函数参数似乎没有帮助

所以,我的问题是:我可以定义一个与构造函数参数同名但可能是不同值的属性吗?如果是,怎么做?若否,原因为何


谢谢

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

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

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

这样使用时,
initValue
不会成为创建的实例的一部分。但是,如果在<代码> DEF或模式匹配声明中使用它,那么它将成为类的每个实例的一部分。

< P>可以考虑参数字段

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
...
类非负双精度(val值:双精度,私有val名称:字符串){
如果(值<0)抛出新的IllegalArgumentException(“值不能为负”)
覆盖def toString=
“非负双(值=%s,名称=%s)”格式(值,名称)
}
val tom=“汤姆”
val k=-2.3
val a=新的非负双倍(k.abs,tom)
a:NonNegativeDouble=NonNegativeDouble(值=2.3,名称=Tom)
a、 价值观
res13:Double=2.3
a、 名字
:12:错误:无法在NonNegativeDouble中访问类NonNegativeDouble中的值名称
a、 名字
val b=新的非负双倍(k,tom)
java.lang.IllegalArgumentException:值不能为负
...
它使用相同的名称“值”、“名称”定义字段和参数。 您可以添加修改器,例如private…

@Daniel C.Sobral

class NonNegativeDouble(initValue: Double) {
  val value = Math.abs(initValue)
}
您的代码是正确的,但“构造函数参数是属性”,这是不正确的

,

如果类Foo(x:Int)这样的参数是 在一个或多个方法中引用

马丁的回答证实了这一点:

这都是事实,但它应该被视为一个实现 技术。这就是为什么规范对此保持沉默的原因

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

如果任何形式参数前面有val,编译器会自动生成getter定义。如果是var,则会另外生成setter。请参见语言规范第5.3节


这些都是关于主构造函数参数的。

对于
案例类
来说,应该是:

case class NonNegativeDouble(private val initValue: Double) {
  val value = Math.abs(initValue)
  def copy(value: Double = this.value) = NonNegativeDouble(value)
}
需要实现
copy
,以防止绑定
initValue
参数的编译器的指定版本


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

除非构造函数是一个case类或用val或var注释,否则它不会成为一个属性。但是它可以在类定义中的任何地方使用。在这种情况下,构造函数参数有任何类型的命名约定吗?@Bruno-有几种命名约定;我个人喜欢在名字后面加一个0,因为这个0意味着一个起点,而且很简单。(下划线有时在前面或后面,但我个人觉得已经有很多下划线了。)@Geoff这是私有财产。你说的“参数化字段”是什么意思?在您的示例中,我们可以很容易地实例化
非负双精度
,其.value实际上会返回负双精度。@Rogach使用“参数字段”可以创建与构造函数中参数名称同名的字段。我改进了我的榜样。