【问题标题】:Object extends Trait, Class extends Trait, both have to implement methodObject extends Trait, Class extends Trait, 都得实现方法
【发布时间】:2015-10-05 02:53:25
【问题描述】:

我有以下设置:

trait A
{
  def doSomething(): Unit;
}

object B extends A
{
 override def doSomething(): Unit = 
 {
   // Implementation
 }
}

class B(creator: String) extends A
{
 override def doSomething(): Unit =
 {
   B.doSomething() // Now this is just completely unnecessary, but the compiler of course insists upon implementing the method
 }
}

现在你可能想知道我为什么要这样做,为什么我让类也扩展 trait。

问题是,在程序的某个地方有一个 A 的集合。 所以某处:

private val aList: ListBuffer[A] = new ListBuffer[A]

在那里,我还必须放 Bs(在其他衍生物中,即 C 和 D)

所以我不能让B级不扩展它。

由于所有实例的实现都是相同的,我想要使用一个对象。

但是我真的需要这个对象也是有原因的。因为有一个类:

abstract class Worker
{
 def getAType(): A

 def do(): Unit =
 {
   getAType().doSomething()
 }
}

class WorkerA
{
  def getAType(): A =
  {
    return B
  }
}

这里返回 B 的单例/对象。这是在 Worker 中实现 do() 所必需的。

总结一下:

需要对象 B 是因为 do()(工人类)中的通用实现,也因为 doSomething() 永远不会改变。

需要类 B,因为在 BaseType A 的集合中,有不同作者的不同 B 实例。

由于上述原因,对象和类都必须实现特征,我在这里有点进退两难。我找不到看起来更整洁的令人满意的解决方案。

所以,我的问题是(事实证明,作为非母语人士,我应该进一步澄清这一点)

有没有办法让一个类扩展一个特征(或类)并说任何抽象方法的实现都应该在对象而不是类中查找,所以我必须只实现“doSomething()”(从特征)一次(在对象中)?正如我所说,这个特征在这里完成了两个不同的任务。 一个是 BaseType 以便集合可以获取该类的实例。另一个是确保 doSomething() 方法存在于每个对象中的合同。

所以对象 B需要扩展 trait,因为 trait 就像 Java 接口,每个(!)对象 B(或 C,或 D)需要有那个方法。 (所以我看到的唯一选项 -> 定义接口/特征并确保方法存在)

edit:以防万一。我是如何真正解决这个问题的:我实现了两个特征。

现在对于一个类(我需要它的地方)我扩展两者,而对于另一个我只扩展一个。所以我实际上从来不需要实现任何不是绝对必要的方法:)

【问题讨论】:

  • 一个更具体的例子可能会有所帮助,你的代码太笼统了(ABdoSomething,...),很难帮助你。
  • 您的问题是什么?我读了你的要求。拥有一个由对象和类扩展的特征看起来不错。你从不问任何事。您提到您找不到看起来“更整洁”的解决方案。 codereview.stackexchange.com 可以更好地解决您的问题吗?
  • @Madoc:假设代码与代码审查无关。
  • @Jamal 感谢您的澄清。只要什么都不问——而且我认为这个问题不是隐含的和显而易见的——它也不符合 SO 的问答格式。希望作者提出更明确的要求。
  • @Madoc:我希望我的编辑能澄清这一点!

标签: scala object collections singleton traits


【解决方案1】:

正如我在评论部分所写,我真的不清楚你在问什么。 但是,查看您的代码示例,在我看来 trait A 并不是真正需要的。 您可以使用 Scala SDK 已经提供的类型:

object B extends (()=>Unit) {
  def apply() { /* implementation */ }
}

或者,作为一种变体:

object B {
  val aType:()=>Unit = {() => /* implementation */ }
}

在第一种情况下,您可以使用B 访问单例实例,在第二种情况下使用B.aType。 在第二种情况下,不需要显式声明apply 方法。

选择你喜欢的。 基本信息是:如果您只定义一个简单的方法,则不需要特征。 这就是 Scala 函数的用途。

列表类型可能如下所示:

private val aList:ListBuffer[()=>Unit] = ???

(顺便说一句:为什么不将其声明为Seq[()=>Unit]?它是ListBuffer 而不是其他类型的序列对调用者很重要吗?)

你的工人可能看起来像这样:

abstract class Worker {
  def aType:()=>Unit // no need for the `get` prefix here, or the empty parameter list
  def do() {aType()}
}

请注意,现在Worker 类型已成为提供调用函数的方法的类。 所以,真的没有必要有一个Worker 类。 您可以直接获取函数 (aType) 并调用它,就这样。

如果您总是想调用object B 中的实现,那么- 就这样做吧。 无需将调用包装在其他类型的实例中。 您的示例类 B 只是将调用转发到 B 对象,这确实是不必要的。 甚至不需要创建B 的实例。 它确实有私有成员变量creator,但由于它从未使用过,因此永远不会以任何方式访问。

所以,我建议完全删除class B。 您只需要()=>Unit 类型,这正是您所需要的:一个不带参数也不返回任何内容的函数。

如果你厌倦了一直写()=>Unit,你可以定义一个类型别名,例如在包对象中。 这是我的建议:

type SideEffect = ()=>Unit

那么您可以使用SideEffect 作为()=>Unit 的别名。

这就是我所能做的。 在我看来,这可能不是您想要的。 但也许这会对你有所帮助。 如果您想得到更具体的答案,最好能澄清问题。

【讨论】:

  • 但是我需要一个 trait 作为契约来确保每个子类都有那个方法,对吧?
  • @Teolha ()=>Unit 已经是定义该合同的特征。这是Function0[Unit] 的别名。不需要自己编。你所称的doSomethingFunction0 中被称为apply
  • 我明白了,谢谢你的解释。我看到的问题是,“... extends () => Unit”很好,但对我的同事来说有点不透明。扩展接口(或 scalas 情况下的特征)更有意。如果我们有多个扩展它的类,那么记住“A,好吧,每个 SubBlubb 都需要扩展那个 Blubb-trait”而不是......“SubBlubb extends () => Unit”要容易得多。至少我是这么看的。从技术上讲,它很可能是一份合同,但它是一份人们可能更容易忽视的合同。 (我看到了别名的东西;))
  • @Teolha 啊,我想我明白你的意思了。在某种程度上,这是一个关于您和您的团队在多大程度上接受 Scala 并进入兔子洞的问题。也许您想为来自 Java 背景的刚接触 Scala 的开发人员留出一点空间。我个人的观点:如果你的团队继续使用 Scala,他们无论如何都会学习和使用这样的结构,在未来的某个时候。那么为什么不现在呢?
  • 实际上我会坚持使用 trait(-class/file) 的另一个原因......它现在实现了多个方法:)
【解决方案2】:

object Bclass B 没有太大关系,除了一些特殊规则。

如果您希望重用 doSomething 方法,您应该重用对象中的实现:

class B {
  def doSomething() = B.doSomething()
}

如果您想将object B 指定为class B 的特定实例,那么您应该执行以下操作:

object B extends B("some particular creator") {
...
}

您也不需要override 修饰符,尽管它们可以方便地进行编译器检查。

【讨论】:

    【解决方案3】:

    伴随对象扩展特征的概念对于定义与类本身相关的行为(例如静态方法)而不是类的实例很有用。换句话说,它允许您的静态方法实现接口。这是一个例子:

    import java.nio.ByteBuffer
    
    // a trait to be implemented by the companion object of a class
    // to convey the fixed size of any instance of that class
    trait Sized { def size: Int }
    
    // create a buffer based on the size information provided by the
    // companion object
    def createBuffer(sized: Sized): ByteBuffer = ByteBuffer.allocate(sized.size)
    
    class MyClass(x: Long) {
      def writeTo(buffer: ByteBuffer) { buffer.putLong(x) }
    }
    object MyClass extends Sized {
      def size = java.lang.Long.SIZE / java.lang.Byte.SIZE
    }
    
    // create a buffer with correct sizing for MyClass whose companion
    // object implements Sized. Note that we don't need an instance
    // of MyClass to obtain sizing information.
    val buf = createBuffer(MyClass)
    
    // write an instance of MyClass to the buffer.
    val c = new MyClass(42)
    c.writeTo(buf)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-14
      • 1970-01-01
      • 1970-01-01
      • 2012-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多