在Scala类声明中,是否;加上;关键字指定一个";is-a";关系

在Scala类声明中,是否;加上;关键字指定一个";is-a";关系,scala,inheritance,mixins,traits,Scala,Inheritance,Mixins,Traits,我想获得一些在Scala中使用with关键字的直觉 一位同事编写了一个trait,该trait完全实现了一个自包含函数,然后使用with将其混合到几个类中。我觉得这种做法不对,于是开始了对话。下面是他使用特征和和的方法: trait Download { def download(url: String): String = {...} } class DataSet(...) extends Super(...) with Download { def method(...) { ...

我想获得一些在Scala中使用
with
关键字的直觉

一位同事编写了一个trait,该trait完全实现了一个自包含函数,然后使用
with
将其混合到几个类中。我觉得这种做法不对,于是开始了对话。下面是他使用
特征
的方法:

trait Download { def download(url: String): String = {...} }
class DataSet(...) extends Super(...) with Download {
  def method(...) { ... download(url) ... }
}
<>我从java和C++来到Scala,总是想到<>代码> < /C> >,因为它还给了我从C++中丢失的多重继承,因此只在“IS-A”的各种方法中使用它(如<代码>扩展< /C> >)。他认为语言设计者可以使用一个更明显的保留词,如
isa
,或者重复使用
扩展
,而不是
,如果这是他们的意图的话。他觉得
with
是为了让他脱离正常的继承准则,并说他在一些流行的库中看到过这样的用法

我对此持开放态度:打算如何使用
with
?如果常见用法与预期不同,那么
最常用的用法是什么?这真的只是一个风格问题吗

在上面的例子中,我反对
with
的最大理由是
with
允许您持有对实例的引用,例如
val d:Download=new DataSet(…)
,它提供了一个
d.Download(…)
方法,它与
DataSet
没有任何关系。也就是说:数据集不是下载的。我主要感兴趣的是如何使用
更新的
作为对问题更改的响应

您的第一个示例确实是继承<代码>扩展
在这里表示继承,就像在C中一样++

with
也表示继承,不过还有另一种查看方式。该声明可以解析为:

class DataSet(...) extends [ Super(...) with Download ]
您使用Download
Super作为包含
Super
Download
的线性化成员的单一类型来阅读,这就是您要扩展的内容。这样想是有道理的,因为你可以这样写:

val x: Super with Download = new DataSet(...)
这是一个非常紧密的耦合,继承的
download()
方法现在作为
DataSet
的方法公开

它确实代表了一种
is-a
关系;如果需要
下载
的实例,则可以成功提供
数据集
的实例来执行此任务

所以
DataSet
is-a
Download
。尽管这种用法通常被许多基于类的面向对象语言的纯粹主义者所反对。主要的反对意见是,你应该总是使用最弱的耦合形式(一种实践鼓励,部分原因是它在C++编译时的改进)

如果要继承很多方法,这是最简单的方法。如果一个类中有很多自动生成的代码与一些自定义逻辑混合在一起,那么它就相当方便了。您只需自动生成一个特征并从中继承。在ScalaTest中,它也可以起到很大的作用,可以快速导入一系列实用方法和类型


然而,我不喜欢在这个用例中使用它

为什么??因为有了
下载
这样的名称,这个东西显然是为了使用网络连接,这在单元测试时是一个特别糟糕的主意(tm);你没有办法在模拟版本中替代替代行为

您最好通过
数据集
构造函数提供
下载
的实例。它仍然可以是单例,您可以使用默认参数来避免样板文件:

trait Download {
  def apply(url: String): String
}
object DefaultDownload extends Download {
  def apply(url: String): String = ...
}

class DataSet(..., download: Download = DefaultDownload) extends Super(...) {
  def method(...) { ... download(url) ... }
}

我认为有两种方法:

  • 考虑到它的继承性,它是技术性的。因此,你的论点成立,你应该像你所说的那样谨慎地使用它。我大部分时间都遵循这种方法

  • 将其视为将内容组成类或对象的一种方式。就像你一样,我也不喜欢这样,但在很多图书馆里确实经常发生这种情况。一个例子是ScalaTest,它有很多特性,比如。甚至Martin Odersky(和/或他的团队)在他们的Coursera课程中也这样使用它。因此,这是一种被认可的做法。这是个好主意吗?我不知道,我想时间会证明一切。至少它没有Java中继承的缺点,即您只能通过继承使用一件事情,而必须对其余部分执行其他操作。这可能会限制一些使我们大多数人在许多用例中避免继承的问题


  • 这并没有真正为
    with
    的使用提供任何指导,实际上,您可能会说
    扩展
    with
    都不表示“is-a”继承式关系。相反,它们都只是一次向类添加一组方法的方法。。。不过,我确实同意,在这个具体的例子中,传递
    下载
    比使用
    从trait中获取它要好。我的第一句话说这是继承<代码>扩展
    和带有
    都是继承。我改变了这个问题,删除了备选方案,因为我认为它们只是分散了我真正感兴趣的内容。如果我没弄错的话,你是说
    一起使用是继承,但是继承并没有建立“is-a”关系的模型,对吗?我可以接受,尽管我有不同的意见,下载还可以明确要求实现类型为其提供网络连接,方法是声明一个抽象方法来检索扩展类型要实现的网络连接,或者使用一个明确类型的self-reference@Jean-it c