【问题标题】:How do I implement polymorphism in Scala如何在 Scala 中实现多态性
【发布时间】:2014-02-10 09:17:18
【问题描述】:

我已经学习 Scala 进行 Web 开发已经有一段时间了,但我偶然发现缺少接口。来自 PHP,我在方法级多态性和 IoC 中使用了很多接口,如下所示:

interface iAnimal
{
    function makeVoice();
}

class Cat implements iAnimal
{
    function makeVoice()
    {
        return "Meow";
    }
}

class Dog implements iAnimal
{
    function makeVoice()
    {
        return "Woof!";
    }
}

class Box
{
    private $_animal;

    function __construct(iAnimal $animal)
    {
        $this->_animal = $animal;         
    }

    function makeSound()
    {
        echo $this->_animal->makeVoice();
    }
}

等等,这是一种简单的方法来确保我传递给Box 对象的任何东西都具有我在其他地方调用的makeVoice 方法。现在,我很好奇的是如何使用 Scala 实现类似的功能。我试图搜索这个,但信息非常稀缺。我找到的唯一答案是使用特征,但据我所知,它们用于具体实现,而不是声明。

提前致谢。

【问题讨论】:

  • 您的问题与控制反转无关,与多态性有很大关系。您可能需要改写一下。
  • 你是对的。我把依赖倒置误认为是 IoC。

标签: oop scala


【解决方案1】:

根据其他答案,解决方案是使用特征:

trait Animal {
  def makeVoice(): Unit //no definition, this is abstract!
}

class Cat extends Animal{
  def makeVoice(): Unit = "Meow"
}
class Dog extends Animal{
  def makeVoice(): Unit = "Woof"
}

class Box(animal:Animal) {
  def makeSound() = animal.makeVoice()
}

Scala 中的trait 将直接编译为Java 中的interface。如果它包含任何具体成员,那么这些成员将直接复制到任何继承自 trait 的类中。你可以愉快地使用 Scala trait 作为 Java 的接口,但是你并没有得到具体的功能。


但是...这只是图片的一部分。到目前为止,我们实现的是子类型多态性,Scala 还允许临时多态性(a.k.a typeclasses):

// Note: no common supertype needed here
class Cat { ... }
class Dog { ... }

sealed trait MakesVoice[T] {
  def makeVoice(): Unit
}
object MakesVoice {
  implicit object CatMakesVoice extends MakesVoice[Cat] {
    def makeVoice(): Unit = "Meow"
  }
  implicit object DogMakesVoice extends MakesVoice[Dog] {
    def makeVoice(): Unit = "Woof"
  }
  //helper method, not required, but nice to have
  def makesVoice[T](implicit mv: MakesVoice[T]) = mv
}

import MakesVoice._
//context-bound version
class Box[T : MakesVoice] {
  //using a helper:
  def makeSound() = makesVoice[T].makeVoice()
  //direct:
  def makeSound() = implicitly(MakesVoice[T]).makeVoice()
}

//using an implicit param
class Box[T](implicit mv : MakesVoice[T]) {
  def makeSound() = mv.makeVoice()
}

这里重要的是MakesVoice 类型类可以与任何 类型相关联,而不管它属于什么层次结构。您甚至可以将类型类与从 3rd 方库中导入的原语或类型一起使用,而这些类型您不可能使用新接口进行改造。

当然,你也有参数多态性,你可能更清楚地知道它是“泛型”:)

【讨论】:

  • 非常好的评论,但有点令人兴奋,就像 Scala 一样。 implicit 是一个非常强大的魔法,需要一段时间才能适应。
  • 它们足够安全,您只需要明智地使用它们并有一点纪律 - 与 any 语言功能非常相似。所以类型类很好,丰富类型很好,但是在普通类型上滥用隐式转换是非常非常糟糕的,
【解决方案2】:

特征用于声明和具体实现。这是您的示例的直接翻译

trait Animal {
  def makeVoice()
}

class Cat extends Animal{
  override def makeVoice(): Unit = "Meow"
}
class Dog extends Animal{
  override def makeVoice(): Unit = "Woof"
}

class Box(animal:Animal) {
  def makeSound()={
    animal.makeVoice()
  }
}

此外,您可以直接在 trait 中实际定义具体实现,这对于不同类层次结构成员共享的行为很有用:

trait Fish{
  def swim()="Swim"
}
class Truit extends Fish 

trait Bird{
  def fly() = "Fly"
}
class Eagle extends Bird

class Duck extends ???{
  def swim=???
  def fly=???
}

鸭子既会游泳又会飞,你可以这样定义:

trait Swimmer{
  def swim
}
trait Fish extends Swimmer{
  def swim()="Swim"
}
class Truit extends Fish 
trait Bird{
  def fly()="Fly"
}
class Eagle extends Bird

class Duck extends Bird with Swimmer

【讨论】:

  • 感谢您的详细回答。
猜你喜欢
  • 1970-01-01
  • 2014-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-18
相关资源
最近更新 更多