【问题标题】:if methods defined in structural type 'B' are a superset of structural type 'A', why can't I pass a 'B' to a method that wants an 'A'?如果结构类型“B”中定义的方法是结构类型“A”的超集,为什么我不能将“B”传递给需要“A”的方法?
【发布时间】:2015-10-23 04:19:19
【问题描述】:

作为学习 Scala 的“蛋糕”模式的一部分,我正在试验类型变量和结构类型。

下面是一个说明我的问题的玩具 API:

当结构类型“B”中定义的方法是 结构类型“A”,为什么我不能将 B 的实例传递给需要“A”的方法?

...  Toy API ....

object Tester extends App {

  trait SomeApi {
    type Organism <: {
      def die(): Unit;
    }

    type Dog <: {
      def die(): Unit;
      def bark(): Unit;
    }

    def dieQuietlyDoesntCompile(entity: Organism): Unit = {
      entity.die()
    }

   def dieQuietly(entity: { def die(): Unit }): Unit = {
      entity.die()
   }

    def processDog(dog: Dog): Unit = {
      println("start dog process on : " + dog)
      dieQuietly(dog)                            
    }
  }
}

我的 API 中的结构类型从您可能称之为“基本类型”(上例中的有机体)开始,另外,我 API 中有其他类型扩展基本类型。在 Toy API 的情况下,Dog 拥有 Organism 的所有方法,另外还有 还有一个:'bark()'。

我想编写一些对基本类型进行操作的辅助方法,如 processDog() 方法 .... 它接受一个“Dog”实例,但它也想调用“dieQuietly” 处理更通用的类型“有机体”。

我上面做的事情的方式是可行的,但是它真的很笨拙,因为我必须完全重复所有的方法 基础结构类型。在这个玩具案例中还不错(因为我只有一个方法:die()),但对于这些结构类型中的方法数量来说真的很尴尬 增加。

因此,我宁愿将 dog 实例传递给类似“dieQuietlyDoesntCompile()”这样编写的方法。
但正如该函数名称所示,如果我将 Dog 实例传递给它,则它无法编译并出现错误:

类型不匹配;找到:dog.type(具有基础类型 SomeApi.this.Dog) 需要:SomeApi.this.Organism

谁能建议一种更方便的方法来实现我的目标...?还是我坚持重复基本类型中的方法? (一种对我来说似乎不太干燥的方法)。在此先感谢您的帮助 ! /克里斯

【问题讨论】:

    标签: scala polymorphism duck-typing


    【解决方案1】:

    您的问题是您使用类型绑定&lt;: 来定义DogOrganism。您使用die() 方法将它们限制为类的子类,这使得它们不相关。

    让我用常规类型来说明它:

    trait Mortal // suppose this trait is analogue of { def die():Unit }
    class Organism extends Mortal
    class Dog extends  Mortal
    
    def die(o:Organism) {}
    
    die(new Dog)  // obviously will not compile
    

    您的代码可以通过定义 Organism 来轻松修复,而无需类型限制:

      type Organism = {
        def die(): Unit;
      }
    

    【讨论】:

    • 优点。不幸的是,我需要弄清楚如何使用结构类型而不是类,因为我正在针对现有代码库进行编程。这个代码库实际上来自 Akka 上的 coursera 类。不知道链接会持续多久,但这里是完整的:github.com/tonyskn/coursera-reactive/blob/master/w4-suggestions/… - 这是另一个 S.O.关于同一代码的问题:stackoverflow.com/questions/20508529/…
    • @ChrisBedford,我想我回答了你的问题。 why can't I pass an instance of B to a method that wants an 'A'? Can anyone suggest a more convenient way to accomplish my goal Organism 的简单修复。如果您想了解有关您在 github 上的代码 sn-p 的特定信息,请询问。我几年前完成了这门课程,也许我能回忆起一些事情。
    【解决方案2】:

    稍微扩展一下 Aivean 的回答:

    如果在结构类型“B”中定义的方法是在结构类型“A”中定义的方法的超集,为什么我不能将 B 的实例传递给需要“A”的方法?

    你可以,但在这种情况下它们不是。 Organism 不是结构类型,它是抽象类型成员,必须是结构类型的子类型。 SomeApi 的一种可能实现是

    class ApiImpl extends SomeApi {
    
      type Organism = {
        def die(): Unit
        def live(): Unit
      }
    
      type Dog = {
        def die(): Unit
        def bark(): Unit
      }
    
      override def dieQuietlyDoesntCompile(entity: Organism): Unit = {
        entity.live()
      }
    }
    

    希望很明显为什么你不能在这里将Dog 传递给dieQuietlyDoesntCompile;但这也意味着你不能在原来的情况下通过它,或者你可以这样做

     val api: SomeApi = new ApiImpl
    
     val dog: api.Dog = ...
    
     api.dieQuietlyDoesntCompile(dog)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-25
      • 1970-01-01
      • 1970-01-01
      • 2013-06-12
      • 2021-08-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多