通过玩对象层次结构来学习Scala,并得出以下结论:
trait LogItem {
override def toString = getClass.getName
}
class LogItemOne extends LogItem {}
class LogItemTwo extends LogItem {}
class LogService {
private def addLogItem(item: LogItemOne) = { println(item.toString) }
private def addLogItem(item: LogItemTwo) = { println(item.toString) }
def addLogItem[A <: LogItem] (item: A): Unit = { addLogItem(item) }
}
下面的测试导致StackOverflowError
"Log service" should "polymorphically add log item" in {
new LogService().addLogItem(new LogItemOne())
new LogService().addLogItem(new LogItemTwo())
}
是否有可能多态地调度到正确的addLogItem
方法,而不需要LogService
的客户端为特定类型的LogItem
调用特定的方法?
据我所知,Scala没有对方法参数执行动态调度(与Java相同)。换句话说,您的代码不会多态地为给定的LogItem
选择正确的方法。相反,由于A
擦除到LogItem
,它将再次递归调用addLogItem[A <: LogItem](item: A)
,这将导致StackOverflow
。
在参数上"伪造"动态分派的一种方法是使用访问者模式。然而,在这种情况下,最好的解决方案可能是使用Scala强大的match
语句:
class LogService {
private def addLogItemOne(item: LogItemOne) = { println(item.toString) }
private def addLogItemTwo(item: LogItemTwo) = { println(item.toString) }
def addLogItem(item: LogItem): Unit = item match {
case one: LogItemOne => addLogItemOne(one)
case two: LogItemTwo => addLogItemTwo(two)
}
}
实际上,如果我删除addLogItem[A <: LogItem] (item: A): Unit
方法并打开其他方法的可见性,它就可以工作了。所以从本质上讲,LogService
本身就变成了一个访问者。
class LogService {
def addLogItem(item: LogItemOne) = { println(item.toString) }
def addLogItem(item: LogItemTwo) = { println(item.toString) }
}