【问题标题】:What is the rationale behind having companion objects in Scala?在 Scala 中拥有伴生对象的基本原理是什么?
【发布时间】:2010-10-11 05:15:02
【问题描述】:

是否存在需要类的伴随对象(单例)的情况?为什么我要创建一个类,比如 Foo 并为它创建一个伴生对象?

【问题讨论】:

标签: oop scala companion-object


【解决方案1】:

伴随对象基本上提供了一个可以放置“类静态”方法的地方。此外,伴随对象或伴随模块可以完全访问类成员,包括私有成员。

伴随对象非常适合封装工厂方法之类的东西。例如,不必在任何地方都有FooFooFactory,您可以让一个带有伴随对象的类承担工厂职责。

【讨论】:

    【解决方案2】:

    除了 Saem 在 his reply 中所说的内容之外,Scala 编译器还在相应的伴随对象(源或目标)中查找类型的 implicit conversions,因此不需要进行转换进口的。

    关于一般单例对象的原因Programming in Scala 说:

    如第 1 章所述,Scala 比 Java 更面向对象的一种方式是 Scala 中的类不能有静态成员。相反,Scala 有单例对象(第 65 页)。

    【讨论】:

      【解决方案3】:

      ...它是存储伴随类的静态工厂方法(不是那个 DP)的好地方。如果您将这些重载的工厂方法命名为 apply(/.../),您将能够创建/初始化您的类

      1. 没有“新”(不是很重要)

      2. 具有不同的可能参数集(与 Bloch 在 Effective Java 中写的关于伸缩构造函数的内容相比)

      3. 能够决定要创建哪个派生类,而不是抽象的(伴随的)派生类

      示例代码:

      abstract class AbstractClass;
      class RealThing(s: String) extends AbstractClass;
      class AlternativeThing(i: Int) extends AbstractClass;
      object AbstractClass {
        def apply(s: String) = {
          new RealThing(s)
        }
        def apply(i: Int) = {
          new AlternativeThing(i)
        }
      }
      
      // somewhere else you can
      val vs = AbstractClass("asdf")  // gives you the RealThing wrapped over string
      val vi = AbstractClass(123)  // gives you AlternativeThing wrapped over int
      

      我不会调用对象/基类 AbstractXxxxx 因为它看起来不错:就像创建抽象的东西一样。给这些名字一个真正的意义。 考虑使用不可变的、方法较少的案例类并封装抽象基类。

      【讨论】:

      • RealThingAlternativeThing 类应该有一个 private 构造函数来强制用户使用 AbstractClass 有工厂。 class AlternativeThing private(i: Int) extends AbstractClass
      • @[Szymon Jachim] Scala 抽象类不支持多重继承。那么为什么编译器在你的情况下允许它呢?
      【解决方案4】:

      伴随对象可用于存储类的所有实例共有的状态和方法但它们不使用静态方法或字段。它们使用可以通过继承覆盖的常规虚拟方法。 Scala 确实没有什么静态的。有很多方法可以使用它,但这里是一个简单的例子。

      abstract class AnimalCounter
      {
          var animals = 0
      
          def name: String
      
          def count()
          {
              animals += 1
              println("%d %ss created so far".format(animals, name))
          }
      }
      
      abstract class Animal
      {
          def companion: AnimalCounter
          companion.count()
      }
      
      object Dog extends AnimalCounter
      {
          val name = "dog"
      }
      
      class Dog extends Animal
      {
          def companion = Dog
      }
      
      object Cat extends AnimalCounter
      {
          val name = "cat"
      }
      
      class Cat extends Animal
      {
          def companion = Cat
      }
      

      产生这个输出:

      scala> new Dog
      1 dogs created so far
      
      scala> new Cat
      1 cats created so far
      
      scala> new Dog
      2 dogs created so far
      
      scala> new Cat
      2 cats created so far
      

      【讨论】:

      【解决方案5】:

      我总是将伴生对象视为在 Scala 中编写函数式和面向对象代码的桥梁。很多时候,我们只需要接受一些输入并提供处理结果的纯函数。将这些相关函数放在伴随对象中可以很容易地查找和使用,对我自己以及在我的代码之上构建的一些人来说。

      此外,无需任何操作即可编写单例模式是一种语言提供的功能。当您需要一个单例来为 JVM 的生命周期封装委托者时,这尤其有用。例如,在 Scala 中编写一个简单的 HTTP 客户端库,您可以在其中封装基于 Java 实现的底层委托,并让您的 API 的使用者生活在纯粹的世界中。

      【讨论】:

        【解决方案6】:

        如果您在同一个文件中以相同的名称定义类和对象,则它们称为伴生类和对象。 Scala 没有 static 作为 JAVA 关键字,您可以将静态替换为 Scala 中的伴生类和对象。

        更多详细信息请查看文章 class and object keyword in scala programming

        【讨论】:

          【解决方案7】:

          首先,它提供了静态方法与非静态方法的明确区分。还提供了一种创建单例类的简单方法。

          它还可以从其他类和/或特征继承方法,这是Java静态方法无法做到的。可以作为参数传递。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-08-12
            • 2020-02-23
            • 1970-01-01
            • 2018-02-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多