问题 mixins和继承有什么区别?


我试图在scala的上下文中理解Mixins。特别是我想知道继承概念和Mixins之间的区别。 Wiki说mixins和inheritance的概念之间有重要的区别,因此我想了解它。

Mixin的定义 维基 说:

mixin类充当父类,包含所需的功能。然后,子类可以继承或简单地重用此功能, 但不是专业化的手段。通常,mixin会将所需的功能导出到子类,而不会创建严格的单个“是”关系。这里有mixins和inheritance概念之间的重要区别,因为子类仍然可以继承父类的所有功能,但是, 关于孩子“成为一种”父母的语义不必一定适用

在上面的定义中,我无法理解以粗体标出的语句。这是什么意思

  1. 子类可以在mixin中继承功能,但不能作为专业化的手段
  2. 在mixins中,子进程继承了父类的所有功能,但是关于子进程的语义“不是必须应用父进程”。 - 孩子如何延伸父母而不一定是父母?有这样的例子吗?

提前感谢您对上述内容的任何澄清。


8922
2018-04-20 01:37


起源

在Scala中,将Mixins视为一个简洁的编译时转换,它将使用额外的方法来装饰某些类型。在这种情况下,虽然Scala跟踪类型检查器的“混合类型”,但方法定义本身也是如此 扁平 到实际类型中,因此在JVM类中没有建立父子。继承通常与运行时多态方法解析相关联 - 但mixins(很大程度上)是正交概念。 Java 8接口默认方法也是mixins。 - user2864740
这个问题似乎确实如此 之前在更广泛的背景下问过 (虽然我对那里的答案感到不满意)。 - badcook


答案:


我不确定我是否正确地理解了你的问题,但是如果我这样做了,你就会问一些东西如何能够继承,而不会像继承那样具有同样的意义。

但是,Mixins不是继承 - 它实际上更类似于动态地将一组方法添加到对象中。虽然继承说“这件事情是另一回事”,但是混合说,“这个对象有另外一些特征。”您可以在用于声明mixins的关键字中看到这一点: trait

从Scala主页公然窃取一个例子:

abstract class Spacecraft {
  def engage(): Unit
}
trait CommandoBridge extends Spacecraft {
  def engage(): Unit = {
    for (_ <- 1 to 3)
      speedUp()
  }
  def speedUp(): Unit
}
trait PulseEngine extends Spacecraft {
  val maxPulse: Int
  var currentPulse: Int = 0
  def speedUp(): Unit = {
    if (currentPulse < maxPulse)
      currentPulse += 1
  }
}
class StarCruiser extends Spacecraft
                     with CommandoBridge
                     with PulseEngine {
  val maxPulse = 200
}

在这种情况下, StarCruiser 不是 CommandoBridge 要么 PulseEngine;它 具有 但是,他们获得了这些特征中定义的方法。它  一个 Spacecraft,正如您所看到的,因为它继承自该类。

值得一提的是,当一个 trait 延伸一个 class,如果你想做点什么 with 这个特质,它必须扩展那个类。例如,如果我有一个 class Dog,我没有 Dog with PulseEngine 除非 Dog 扩展 Spacecraft。这样,它就像添加方法一样;然而,它仍然相似。


9
2018-04-20 03:32



> In this case, the StarCruiser isn't a CommandoBridge or PulseEngine   我真的不明白这是怎么回事,当我可以定义一个方法时,比方说 launch(foo: CommandoBridge) 并称之为 launch(new StarCruiser())...不是传递实例的能力 StarCruiser 期待的方法 CommandoBridge 显示那个的典型例子 StartCruiser 确实是 一个  CommandoBridge? - dade


我认为它非常依赖于使用。 Scala是一种多范式语言,有时会让它变得强大而且有点令人困惑。 我认为当使用正确的方式时,Mixins非常强大。 Mixins应该用于引入行为并减少bolierplate。

Scala中的特性可以有实现,并且很有可能扩展它们并使用它们。

特征可以用于继承。它也可以称为mixins,但在我看来,这不是最好的使用方式 mixin 行为。在这种情况下,您可以将特征视为Java Abstract Classes。其中你得到的子类是超类(特征)的“类型”。

然而Traits可以用作 proper mixins 同样。现在使用特征作为 mixin 取决于“你如何混合它”的实现。主要是问自己一个简单的问题。它是“特质的子类真的是一个 kind 特征或是减少样板的特质行为中的行为“。 通常,最好通过将特征混合到对象而不是扩展特征来创建新类来实现。

例如,请考虑以下示例:

    //All future versions of DAO will extend this
trait AbstractDAO{
  def getRecords:String
  def updateRecords(records:String):Unit
}
//One concrete version
trait concreteDAO extends AbstractDAO{
  override def getRecords={"Here are records"}
  override def updateRecords(records:String){
    println("Updated "+records)
  }
}
//One concrete version
trait concreteDAO1 extends AbstractDAO{
  override def getRecords={"Records returned from DAO2"}
  override def updateRecords(records:String){
    println("Updated via DAO2"+records)
  }
}
//This trait just defines dependencies (in this case an instance of AbstractDAO) and defines operations based over that
trait service{
  this:AbstractDAO =>

  def updateRecordsViaDAO(record:String)={  
  updateRecords(record) 
  }
  def getRecordsViaDAO={
  getRecords
  }
}


object DI extends App{
  val wiredObject = new service with concreteDAO //injecting concrete DAO to the service and calling methods
  wiredObject.updateRecords("RECORD1")
  println(wiredObject.getRecords)

  val wiredObject1 = new service with concreteDAO1
  wiredObject1.updateRecords("RECORD2")
  println(wiredObject1.getRecords)

}

concreteDAO 是一个延伸的特征 AbstractDAO  - 这是继承

val wiredObject = new service with concreteDAO  - 这是适当的mixin行为 由于服务特征要求 mixin 一个 AbstractDAO。这是错的 Service 扩展 ConcreteDAO 反正因为 service 需要 AbstractDAO 它不是一种类型 AbstractDAO。 而是创建的实例 service 与不同的mixin。


3
2018-04-20 12:55





一个 特征 (这就是所谓的 混入 当与类混合时)就像Java中的接口(虽然有很多 分歧)您可以在其中添加其他功能,而不必具有“是一种”关系。或者你可以这么说 通常 traits捆绑了可由多个独立类使用的功能。

举个例子来自Scala库, 有序[A] 是一个 trait 它为一些基本的比较操作提供了实现(比如 <<=>>=)可以拥有自然排序数据的类。

例如,假设您有自己的课程 Number 和子类 EvenNumber 和 OddNumber 如下所示。

class Number(val num : Int) extends Ordered[Number] {
  override def compare(that : Number) = this.num - that.num
}

trait Half extends Number {
  def half() = num / 2
}

trait Increment extends Number {
  def increment() = num + 1
}

class EvenNumber(val evenNum : Int) extends Number(evenNum) with Half

class OddNumber(val oddNum : Int) extends Number(oddNum) with Increment

在上面的例子中,类 EvenNumber 和 OddNumber 分享 是一个 有关系 Number 但 EvenNumber 没有“是”的关系 Half 也不 OddNumber 分享“是一种”与...的关系 Increment

另一个 很重要的一点 即使是上课 Number 使用 extends Ordered 语法,这意味着 Number 有一个 含蓄  是一个 与超类的关系 Ordered 即 Any


2
2018-04-20 07:11





我认为它是在讨论实际的类层次结构。例如,a Dog 是一种类型 Animal 如果它从类扩展(继承)。它可以在任何地方使用 Animal 参数适用。


1
2018-04-20 02:40