5

我有一个问题,以下代码中名为 fooSome 的两个方法无法编译,因为编译器报告了重复方法名称的问题:

class Foo() {

  // variable block has 2 closure variables
  def fooSome(block: Some[(Int, String) => Unit]) = {

  }

  // variable block has 1 closure variables
  def fooSome(block: Some[Int => Unit]) = {

  }

  // variable block has 2 closure variables
  def fooNoSome(block: (Int, String) => Unit) = {

  }

  // variable block has 1 closure variables
  def fooNoSome(block: Int => Unit) = {

  }
}

相反,编译器报告没有与名为 fooNoSome 的两个方法发生此类方法名称冲突。所以问题是编译器没有看到“Some[(Int,String)=> Unit]”和“Some[(Int)=> Unit]”之间的区别,而“(Int,String)=> Unit”对于 fooNoSome 方法,被视为与 "( Int ) => Unit" 不同的签名。

我可以通过创建一个用于“Some[(Int, String) => Unit]”案例的类 Some2Args 和一个用于“Some[(Int) => Unit]”案例的类 Some1Arg 来解决这个问题。

我的问题是是否有更优雅、更省力的解决方案。

4

2 回答 2

8

编译器确实看到了它们之间的差异,只是不允许在重载中使用这种差异(因为和的擦除是相同的,并且当参数的擦除相同时JVM不允许重载)。解决方案是添加假的隐式参数:Some[(Int, String) => Unit]Some[Int => Unit]

class Foo() {
  def fooSome(block: Some[(Int, String) => Unit]) = {

  }

  def fooSome(block: Some[Int => Unit])(implicit d: DummyImplicit) = {

  }
}

还要注意fooNoSomearefooNoSome(Function2)和的擦除fooNoSome(Function1),所以如果你想添加另一个重载,它接受一个或两个参数的任何函数,你需要DummyImplicit再次使用这个技巧:

  def fooNoSome(block: Double => Unit)(implicit d: DummyImplicit) = ...
于 2014-01-27T08:36:59.157 回答
2

一个更系统的方法,没有虚拟的隐式,将是这样的:

class Foo() {
  def fooSome[X](block: Some[X])(implicit cfs: CanFooSome[X]) = {
    cfs.handleThat(block)
  }
}

trait CanFooSome[X] {
  def handleThat(block: Some[X]): Unit // or other return type
}

// in carefully chosen scope for implicits
implicit object CanFooSomeIntToUnit extends CanFooSome[Int => Unit] {
  def handleThat(block: Some[Int => Unit]) = ...
}

// same for all other type combinations, like (Int, String) => Unit etc.

这是因为 Scala 编译器在编译时确定要为 CanFooSome 实现插入什么,而 JVM 在运行时看不到任何内容。这种模式可以具有某些优点(例如可扩展性:可以从外部提供新的 CanFooSome,而无需更改 Foo 的代码),并且不会随着 fooSome 类型数量的增加而变得混乱。

于 2014-01-27T23:42:36.597 回答