奥德斯基的说法是有效且重要的,但有些人不明白他的意思。
假设在 Java 中有一个类 Foo,其方法为 f:
class Foo {
int f() { /* does something great */ }
}
您可以编写一个接受 Foo 并在其上调用 f 的方法:
void g(Foo foo) { foo.f(); }
也许有一个类 SubFoo 扩展了 Foo; g 也适用于此。可以有一整套类,通过继承或接口相关,它们共享可以与 g 一起使用的事实。
现在让我们将 f 方法设为 静态:
class Foo {
static int f() { /* does something great */ }
}
我们可以将这个新的 Foo 与 g 一起使用吗,也许像这样?
g(Foo); // No, this is nonsense.
该死。好的,让我们更改 g 的签名,以便我们可以将 Foo 传递给它并让它调用 f。
糟糕——我们不能。我们不能传递对 Foo 的引用,因为 Foo 不是某个类的实例。在这里评论的一些人对存在与 Foo 对应的 Class 对象这一事实感到困惑,但正如 Sotirios 试图解释的那样,Class 对象没有 f 方法,而 Foo 不是该类的实例。 Foo 不是任何事物的实例;它根本不是一个对象。 Foo 的 Class 对象是 Class Class 的实例,其中包含有关 Foo 的信息(将其视为 Foo 的内部 Wikipedia 页面),并且与讨论完全无关。 “老虎”的维基百科页面不是老虎。
在 Java 中,像 3 和 'x' 这样的“原语”不是对象。它们是 Scala 中的对象。为了提高性能,您的程序将在执行期间尽可能使用 JVM 原语 3 和“x”,但在您编写代码的级别上,它们实际上是对象。它们在 Java 中不是 对象这一事实对于任何试图编写处理所有数据类型的代码的人来说都是相当不幸的后果——你必须有特殊的逻辑和额外的方法来覆盖原语。如果你曾经见过或写过这种代码,你就会知道它很糟糕。奥德斯基的说法不是“纯粹主义”;远非如此。
在 Scala 中,没有任何运行时数据不是对象,也没有可以调用方法的东西不是对象。在 Java 中,这些陈述都不是真的; Java 是一种部分面向对象的语言。在 Java 中有些东西不是对象,有些方法不在对象上。
Scala 的新手经常认为object Foo 是 Java 静态的一些奇怪的替代品,但这是您需要尽快克服的问题。而是将 Java 的静态方法视为非 OO 疣,而将 Scala 的 object Foo { ... } 视为类似以下内容:
class SomeHiddenClass { ... }
val Foo = new SomeHiddenClass // the only instance of it
这里的Foo是一个value,而不是一个type,它确实是一个对象。它可以传递给一个方法。它可以扩展一些其他类。例如:
abstract class AbFoo { def f:Int }
object Foo extends AbFoo { def f = 2 }
现在,终于可以说
g(Foo)
确实,类的“伴生对象”是放置类的非实例方法和数据的好地方。但是那个伴生对象是一个对象,所以通常的规则和能力适用。
在 Java 中,您将此类方法放在非对象上——限制了它们的使用方式——这一事实是一种责任,而不是一种特性。这肯定是不是 OO。