我有以下代码:
import com.github.nscala_time.time.Imports._
class Account {
def balance(date: DateTime): Double = {
/* some logic that calculates balance on given date */
val calculatedBalance = 42
calculatedBalance
}
def balance: Double = balance(DateTime.now)
}
class Deposit(val interestRate: Double) extends Account {
override def balance(date: DateTime): Double = {
/* some logic that calculates balance for deposit account */
val calculatedBalance = 100 * interestRate;
calculatedBalance
}
}
我尝试以如下方式使用这些类:
val simpleAccount = new Account
val depositAccount = new Deposit(0.1)
val simpleBalanceOnDate = simpleAccount.balance(DateTime.now + 1.month) // A
val depositBalanceOnDate = depositAccount.balance(DateTime.now + 1.month) // B
val simpleBalance = simpleAccount.balance // C
val depositBalance = depositAccount.balance // D
情况A
, B
和C
编译没有任何错误,但对于D
行,我看到错误消息:
Error:(28, 38) ambiguous reference to overloaded definition,
both method balance in class Deposit of type (date: com.github.nscala_time.time.Imports.DateTime)Double
and method balance in class Account of type => Double
match expected type ?
val depositBalance = depositAccount.balance
^
你能解释一下为什么D
有编译错误,而C
没有编译错误吗?
提前感谢!
我想编译器对无参数方法继承感到困惑,尽管我不能诚实地解释为什么,对于一个快速的解决方案,这应该工作:
class Account {
{ ... }
def balance(): Double = balance(DateTime.now)
}
val depositAccount = new Deposit(0.1)
val depositBalance = depositAccount.balance()
为什么会发生这种情况对我来说很模糊,也许有人知道scala编译器是如何看到无参数方法继承的。
也阅读周围,特别是在Scala编程:
这样的无参数方法在Scala中很常见。相比之下,用空括号定义的方法,如def height(): Int,被称为空父方法。建议的约定是,只要没有参数,并且方法仅通过读取包含对象的字段来访问可变状态(特别是,它不改变可变状态),就使用无参数方法。该约定支持统一访问原则1,即客户端代码不应受到将属性实现为字段或方法的决定的影响。例如,我们可以选择将宽度和高度作为字段而不是方法来实现,只需将每个定义中的定义更改为val:
abstract class Element {
def contents: Array[String]
val height = contents.length
val width =
if (height == 0) 0 else contents(0).length
}
从客户机的角度来看,这两对定义是完全等价的。唯一的区别是字段访问可能比方法调用稍微快一些,因为字段值是在初始化类时预先计算的,而不是在每次方法调用时计算的。另一方面,字段在每个Element对象中需要额外的内存空间。因此,属性是更好地表示为字段还是方法取决于类的使用配置文件,并且使用配置文件可能会随着时间的推移而改变。关键是当Element类的内部实现发生变化时,它的客户端不应该受到影响。
为什么不提供一个这样的默认参数:
class Account {
def balance(date: DateTime = DateTime.now): Double = {
/* some logic that calculates balance on given date */
val calculatedBalance = 42
calculatedBalance
}
}
class Deposit(val interestRate: Double) extends Account {
override def balance(date: DateTime): Double = {
/* some logic that calculates balance for deposit account */
val calculatedBalance = 100 * interestRate;
calculatedBalance
}
}
val simpleBalanceOnDate = simpleAccount.balance(1) // A
val depositBalanceOnDate = depositAccount.balance(1) // B
val simpleBalance = simpleAccount.balance() // C
val depositBalance = depositAccount.balance() // D