在Scala中,为什么要使用伴生对象?

121

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


3
请参见 http://programmers.stackexchange.com/questions/179390/what-are-the-advantages-of-scalas-companion-objects-vs-static-methods。 - Vadzim
请参见https://dev59.com/T2kw5IYBdhLWcg3wkLX2#9806136,这是这里答案的一个很好的汇编。 - laughedelic
7个回答

92

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

伴生对象非常适合封装工厂方法等内容。不必在各处都有FooFooFactory之类的东西,您可以使一个类及其伴生对象承担工厂职责。


68

伴生对象非常有用,可以存储对所有类实例通用的状态和方法,但它们不使用静态方法或字段。它们使用常规的虚拟方法,这些方法可以通过继承进行覆盖。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

1
类似的插图也可以在这里找到:http://daily-scala.blogspot.sk/2009/09/companion-object.html - xhudik

32

这是一个很好的存储静态工厂方法(不是设计模式)的地方,用于配合类。如果您将那些重载的工厂方法命名为apply(/.../),则可以创建/初始化您的类:

  1. 无需使用'new'关键字(并不是非常重要)

  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,因为它看起来并不好:就像创建一些抽象的东西。给这些名称赋予真正的含义。考虑使用不可变、没有方法的case类,并封闭抽象基类。


2
RealThing and AlternativeThing class should have a private constructor to force the user to use the AbstractClass has factory. class AlternativeThing private(i: Int) extends AbstractClass - metch
Scala抽象类不支持多重继承。那么为什么编译器在你的情况下允许它呢? - user2441441

19
除了Saem在他的回复中提到的内容,Scala编译器还会查找相应伴生对象(源或目标)中类型的隐式转换,因此不需要导入转换。
关于单例对象的一般原因,Programming in Scala说:

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


4
我一直认为伴生对象是在Scala中编写函数式和面向对象代码的桥梁。许多时候,我们只需要纯函数,它们接受一些输入并提供处理结果。将这些相关函数放在伴生对象中使查找和使用变得容易,无论是对于我自己还是对于在我的代码上构建的人来说。
此外,它是一种语言提供的特性,可以在不做任何事情的情况下编写单例模式。当您需要一个单例来封装JVM生命周期的委托者时,这尤其有用。例如,在Scala中编写一个简单的HTTP客户端库,您可以封装一个基于Java实现的委托者,并让API的使用者处于纯净的世界中。

0
如果您在同一文件中使用相同的名称定义类和对象,则它们被称为伴生类和对象。Scala没有像JAVA关键字那样的静态关键字,您可以在Scala中使用伴生类和对象作为静态的替代品。
有关更详细的信息,请查看文章Scala编程中的类和对象关键字

-1

首先,它清晰地区分了静态方法和非静态方法。同时提供了创建单例类的简单方式。

它还可以继承其他类和/或特质的方法,这是Java静态方法无法实现的,并且可以作为参数传递。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接