【问题标题】:Scala - call method on upper bounded instanceScala - 在上限实例上调用方法
【发布时间】:2016-12-15 15:53:30
【问题描述】:

在下面的代码中,有一个 DogSearcher,它有一个名为 fetch 的方法,它需要一个 Ball。我们也可以有一个 CatSearcher,它带有一个期待 Bell 的 fetch 方法。这个想法是我们可以在继承自 PetSearcher 的实例上调用 fetch 并为其提供不同的参数。

知道我错过了什么吗?

trait PetSearcher {
  def search(what: Thing): Unit = {
    println("default searching")
  }
}

class DogSearcher extends PetSearcher {
  def search(what: Ball): Unit = {
    println("dog searching")
  }
}
trait Thing {
  val name: String
}
case class Ball(name: String) extends Thing

class Pet {
  def fetch[S <: PetSearcher, F <: Thing](searcher: S, what: F): Unit = {
    println(what)
    searcher.search(what)
  }
}

class Dog extends Pet {
  val searcher = new DogSearcher()
  val ball = new Ball("red")
  def go(): Unit = {
    fetch[DogSearcher, Ball](searcher, ball)
  }
}

//should use DogSearcher but calls fetch on Search, not on DogSearcher. 
// So prints default searching, not dog searching..
new Dog().go() 

【问题讨论】:

    标签: scala oop generics types scala-generics


    【解决方案1】:

    DogSearch 的 search 方法不会覆盖 PetSearcher 的 search 方法,因为参数类型不同(what 是 PetSearcher 的 Thing 而 DogSearcher 的 Ball - 事实上@987654326 @extends Thing 不足以使函数调用相同)。

    让PetSearcher带一个类型参数来定义what的类型,那么DogSearcher可以覆盖这个(注意override关键字是必须的):

    trait PetSearcher[T] {
      def search(what: T): Unit = {
        println("default searching")
      }
    }
    
    class DogSearcher extends PetSearcher[Ball] {
      override def search(what: Ball): Unit = {
        println("dog searching")
      }
    }
    

    为了让它编译,你还需要更新 PetSearcher 在 Pet 中的使用,通过将类型参数 F(这个 Pet 搜索的 Thing 的子类型)添加到 PetSearcher:

    def fetch[S <: PetSearcher[F], F <: Thing](searcher: S, what: F): Unit = {
    

    【讨论】:

    • 有没有办法说DogSearcher extends PetSearcher[Ball] 然后让search 方法知道应该接受Ball 而无需手动将Ball 添加到方法签名中?如果你有 20 种方法,你不会想到处复制 Ball
    • 我不这么认为,在 scala 中你必须给出方法参数的类型。如果你想避免打字,一个好的 IDE 应该帮助,使用带有 scala 插件的 IntelliJ,覆盖一个方法(如示例中,替换已经实现的特征)按 ctrl+o 并选择方法 - 它将填写类型(包括在这种情况下类型派生自参数的情况)。要实现方法(即如果 trait 没有默认实现)按 ctrl+i(这个快捷键在实现 trait 时非常有用)。
    【解决方案2】:

    作为@JamesThorniley's answer 的替代方案,您可以定义search 以接受扩展Thing 的任何内容:

    trait PetSearcher {
        def search[T <: Thing](what: T): Unit = {
            println("default searching")
        }
    }
    
    class  DogSearcher extends PetSearcher {
        override def search[T <: Thing](what: T): Unit = {
            println("dog searching")
        }
    }
    

    这会根据需要输出dog searching。

    【讨论】:

    • 虽然这可行,但我实际上更喜欢@JamesThorniley 的答案,因为它限制了 DogSearcher 可以搜索的内容。还是谢谢你,这在其他情况下可能有用
    • @Sofia 当然,这完全取决于您的用例。如果每个 PetSearcher 只应该能够查找单个项目类型,那么您将使用该类型参数化该类。
    • @Sofia 顺便说一句,fetch[DogSearcher, Ball](searcher, ball) 可以写成fetch(searcher, ball),在这里添加类型是多余的。
    猜你喜欢
    • 2012-11-02
    • 2021-12-02
    • 2014-11-21
    • 2010-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多