Scala重载不选择最具体的方法

时间:2014-11-16 12:57:04

标签: java scala oop overloading

拥有Java类:

class abstract Exp
class ExpAdd extends Exp
class ExpSub extends Exp

和Scala方法:

def interpret(e: ExpAdd) = interpret(e.left_arg) + interpret(e.right_arg)
def interpret(e: ExpSub) = interpret(e.left_arg) - interpret(e.right_arg)

def interpret(exp: Exp) = exp match {
  case e : ExpAdd = interpret(e)
  case e : ExpSub = interpret(e)
}

如何重构代码使更轻松地添加新的Exps ?我认为解释(Exp)根本不会被使用,因为另外两个更具体和

  

如果多个成员方法都可访问且适用于a   方法调用,有必要选择一个提供   运行时方法调度的描述符。 Java编程   language使用选择最具体方法的规则。

然而,在Scala中似乎并非如此。我也尝试删除解释(Exp),但编译器并不喜欢它。这就是我使用case子句的原因......有没有办法让重载按预期工作?或者可能是一些解决方法?

3 个答案:

答案 0 :(得分:2)

正如@Alexy所说,方法解析基于编译时或"静态"参数的类型。您可以将操作数的类型作为表达式类型的一部分,然后使用implicits以递归方式构造给定类型的解释器:

trait Exp
case class ExpAdd[A <: Exp, B <: Exp](left_arg: A, right_arg: B) extends Exp
case class ExpSub[A <: Exp, B <: Exp](left_arg: A, right_arg: B) extends Exp
case class Literal(i: Int) extends Exp

trait ExpInterpreter[E <: Exp] {
  def interpret(e: E): Int
}
object ExpInterpreter {
  implicit object LiteralInterpreter extends Interpreter[Literal] {
    def interpret(l: Literal) = l.i
  }
  implicit def addInterpreter[A <: Exp, B <: Exp](
    implicit leftInterpreter: Interpreter[A], rightInterpreter: Interpreter[B]) =
    new Interpreter[ExpAdd[A, B]] {
      def interpret(e: ExpAdd[A, B]) = leftInterpreter.interpret(e.left_arg) +
        rightInterpreter.interpret(e.right_arg)
    }
  implicit def subInterpreter ...
}

然后,当您添加新的Exp个案例时,您只需要定义一个新的隐式,并且您可以在任何地方执行此操作,而不必将案例添加到特定的匹配表达式。您甚至可以在库中使用ExpExpInterpreter,在不同的jar中使用案例和特定的解释器。

如上所述,这意味着表达式的结构是其类型的一部分(并且你必须携带类型,直到你解释它 - 没有办法解释泛型Exp,只有E <: Exp: ExpInterpreter),但也许可以使用与Free monad相似的技术来克服这个问题?

答案 1 :(得分:1)

您对Expinterpret的定义并不完全可见,也不确定我理解您要表达的内容,但这里有2个想法:

  • 尝试使用运算符重载定义+-方法以进行更多DSL-esque操作
  • 更好的是,您可以将加法,减法或任何其他操作视为 bahaviours 并将其定义为函数。这种方式(具有多个行为单元的单个数据表示),您的代码将遵循更多FP原则。

答案 2 :(得分:1)

  

如果多个成员方法都可访问并适用于方法调用,则必须选择一个为运行时方法调度提供描述符。 Java编程语言使用选择最具体方法的规则。

在Java和Scala中也是如此:在编译时选择使用哪个重载(然后在运行时将此特定重载调度,如报价说)。由于e.left_arg不知道是ExpAdd还是ExpSub,因此编译器只能选择interpret(exp: Exp)并在删除时抱怨。一种选择是删除其他重载:

def interpret(exp: Exp) = exp match {
  case e : ExpAdd => interpret(e.left_arg) + interpret(e.right_arg)
  case e : ExpSub => interpret(e.left_arg) - interpret(e.right_arg)
}

// when extending
override def interpret(exp: Exp) = exp match {
  case e : ExpMult => interpret(e.left_arg) * interpret(e.right_arg)
  case _ => super.interpret(exp)
}