Scala类成员和构造函数参数名称冲突
考虑以下用Java编写的类: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的最后一个字段,该字段在构造函数中初始化,方法是获取其
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使用“参数字段”可以创建与构造函数中参数名称同名的字段。我改进了我的榜样。