V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
w4ngzhen
V2EX  ›  Scala

关于《Scala 编程(3rd)》中, 20.6 抽象类型 P424 的疑问

  •  
  •   w4ngzhen · 2019-07-03 23:59:28 +08:00 · 5019 次点击
    这是一个创建于 1730 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在《 Scala 编程( 3rd )》中,20.6 抽象类型这一节中,本人有个非常不解的地方,书中提到:

    class Food
    
    abstract class Animal {
      def eat(food: Food)
    }
    
    class Grass extends Food
    
    class Cow extends Animal {
      override def eat(food: Grass) = {}	// 这不能编译
    }					// 如果能的话...
    
    class Fish extends Food
    
    val bessy: Animal = new Cow
    
    bessy eat (new Fish) // ...你就能给牛吃草了
    

    本人理解 override 超类方法,方法参数是不支持协变的,但是上文假设通过编译,"bessy eat (new Fish)"不是本来就不能运行吗? Fish 本来就不能上转型为 Grass 呀。何况如下重写 eat 方法岂不是同样有这个问题?

    class Cow extends Animal {
      override def eat(food: Food): Unit = {}
    }
    
    val cow = new Cow
    
    cow eat new Fish
    // 能通过编译且运行
    

    小弟不才,一直没理解,希望各位大佬轻喷

    18 条回复    2019-07-04 17:32:20 +08:00
    ech0x
        1
    ech0x  
       2019-07-04 07:02:17 +08:00 via iPhone
    class Fish extends Food
    你已经指定 Fish 是可以被牛吃的一种食物了啊。
    ech0x
        2
    ech0x  
       2019-07-04 07:04:20 +08:00 via iPhone
    名字只是一个代称,写什么都是没有问题的,类与类之间的关系才是重要的。
    w4ngzhen
        3
    w4ngzhen  
    OP
       2019-07-04 07:22:29 +08:00 via iPhone
    @ech0x 这位老师您看哈,上述假设的地方假如成立,那么 Cow 确实有一个 eat(Grass)的方法,这个方法依然不能接受 Fish 实例呀
    ech0x
        4
    ech0x  
       2019-07-04 07:27:35 +08:00 via iPhone
    @w4ngzhen 别叫我老师😂我其实不会 Scala。
    仍然不接受 Fish 所以呢?
    ech0x
        5
    ech0x  
       2019-07-04 07:28:28 +08:00 via iPhone
    @w4ngzhen 我没明白你想表达什么。
    w4ngzhen
        6
    w4ngzhen  
    OP
       2019-07-04 08:03:53 +08:00 via iPhone
    @ech0x 所以这样不是更能够限定接受的参数类型吗,但是书中表示如果参数能够协变,反而会出现 cow eat new Fish 的情况
    ech0x
        7
    ech0x  
       2019-07-04 08:38:04 +08:00 via iPhone
    class Cow extends Animal {
    override def eat(food: Fish) = {}
    }
    这样不就出现了
    cow eat new Fish 的情况?
    ech0x
        8
    ech0x  
       2019-07-04 08:39:18 +08:00 via iPhone
    哦,我弄错了,问题不在这里。
    ech0x
        9
    ech0x  
       2019-07-04 08:42:00 +08:00 via iPhone
    我知道问题在哪里了,你注意看 bessy 的类型,bessy 的类型不是 Cow 是 Animal
    w4ngzhen
        10
    w4ngzhen  
    OP
       2019-07-04 09:39:03 +08:00
    @ech0x 额,是否是这样的, [错误假设] 假如成立的时候,能通过编译,但在运行时调用为 eat ( Grass )方法,所以会有转型出错这样的不安全情况?但是这样又说不通 override def eat(food: Food),cow 依然能够 eat Fish 的情况啊。
    madeye
        11
    madeye  
       2019-07-04 09:48:28 +08:00
    简单来说,你不能用

    ···
    def eat(food: Grass)
    ···

    去 override

    ···
    def eat(food: Food)
    ···

    这两个 function 的 signature 并不一样,编译时就会报错。

    ···
    method eat overrides nothing.
    Note: the super classes of class Cow contain the following, non final members named eat:
    def eat(food: Playground.this.Food): Unit
    ···

    https://scastie.scala-lang.org/Gq2lJuhRTnCM8Q46BSmA6g
    dcalsky
        12
    dcalsky  
       2019-07-04 09:53:28 +08:00 via Android
    @w4ngzhen 为什么不编译一下呢
    w4ngzhen
        13
    w4ngzhen  
    OP
       2019-07-04 10:07:50 +08:00
    @dcalsky
    @madeye
    两位老师,这个我自己已经写过了并且也能理解。现在核心就是不能理解书上说的假设能够通过编译后的解释,就拿我第二段代码来说,能够通过编译且能正常运行,这个时候就违背了语义了呀( cow eat Fish ),现在书上意义就是说,我们 [不能] 通过参数协变来 override 超类的方法,因为如果那样,就会出现 cow eat Fish 的情况;但是在我那段代码中(第二段):我也没有使用错误的 override,但是依然出现了 cow eat Fish 的情况。
    ech0x
        14
    ech0x  
       2019-07-04 10:32:20 +08:00 via iPhone
    @madeye 惊现 madeye 大佬。
    dcalsky
        15
    dcalsky  
       2019-07-04 10:38:32 +08:00 via Android
    @w4ngzhen 没问题啊 只不过是你命名语义的问题。第二段代码,你声明了 Cow 可以 Eat Food,而 Fish 是 Food 的子类,所以 fish 可以作为参数传入。我举个例子好了,有一个 array[HTMLDOM],,你把 P,Button,Input 这些 HTMLDOM 的子类 add 进入都没问题。你的 Food 就是对应了 HTMLDOM,而 Fish 对应了这里的 Button,所以是没问题的。
    madeye
        16
    madeye  
       2019-07-04 12:10:30 +08:00
    "假设能够通过" 在英文里是虚拟语态,本意就是这件事是不可能,否则会造成不合理的结果。

    ···
    class Food
    abstract class Animal {
    def eat(food: Food)
    }
    class Grass extends Food
    class Cow extends Animal {
    override def eat(food: Grass) = {} // This won't compile,
    } // but if it did,...
    class Fish extends Food
    val bessy: Animal = new Cow
    bessy eat (new Fish) // ...you could feed fish to cows.
    ···

    英文原文是 “ but if it did,...”,翻译成 "但如果这都能编译过的话" 可能会更好一些。
    w4ngzhen
        17
    w4ngzhen  
    OP
       2019-07-04 13:00:46 +08:00
    @madeye 但是我还是很纠结这个的描述啊,您说“本意就是这件事是不可能,否则会造成不合理的结果。”但是我使用正确的方式 override,依然是不合理的结果呀(见我的第二段代码)。
    madeye
        18
    madeye  
       2019-07-04 17:32:20 +08:00   ❤️ 1
    @w4ngzhen 因为你改变了原来代码的语意,cow 吃的 grass,而不是泛指的 food。严格来说来说,第二段代码算是个 bug,因为没有定义清楚 eat 这个函数所接受的类型。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2893 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 11:14 · PVG 19:14 · LAX 04:14 · JFK 07:14
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.