为什么静态成员使语言不那么面向对象?

我正在学习Scala,我在Odersky的Programming Scala第二版中遇到了这个声明:

Scala比Java更面向对象的一种方式是Scala中的类不能有静态成员。

我对Java或Scala没有足够的经验来理解这种比较。 为什么静态成员使语言少于OO?

奥德斯基的陈述是有效和重要的,但有些人不明白他的意思。

让我们说在Java中你有一个方法f的类Foo:

class Foo { int f() { /* does something great */ } } 

您可以编写一个接受Foo并在其上调用f的方法:

 void g(Foo foo) { foo.f(); } 

也许有一个类SubFoo扩展了Foo; g也适用于此。 可以有一整套类,通过inheritance或接口相关,它们共享它们可以与g一起使用的事实。

现在让我们将f方法设为静态

 class Foo { static int f() { /* does something great */ } } 

我们可以用g这个新的Foo,也许是这样吗?

 g(Foo); // No, this is nonsense. 

该死。 好吧,让我们改变g的签名,以便我们可以将Foo传递给它并让它调用f。

哎呀 – 我们做不到。 我们不能传递对Foo的引用,因为Foo不是某个类的实例 。 这里评论的一些人对于Foo对应的Class对象这一事实感到困惑,但正如Sotirios试图解释的那样,Class对象没有f方法,而Foo不是该类的实例。 Foo不是任何事物的实例; 它根本不是一个对象。 Foo的Class对象是类Class的一个实例,它包含有关Foo的信息(将其视为Foo的内部Wikipedia页面),并且与讨论完全无关 。 “老虎”的维基百科页面不是老虎。

在Java中,像“3”和“x”这样的“基元”不是对象。 它们 Scala 中的对象。 为了提高性能,程序将在执行期间尽可能使用3和’x’的JVM原语,但在您编写的级别它们实际上是对象。 事实上,它们不是 Java中的对象,对于任何试图编写处理所有数据类型的代码的人来说都是相当不幸的结果 – 你必须有特殊的逻辑和其他方法来覆盖原语。 如果您曾经见过或编写过这种代码,那么您就会知道这很糟糕。 奥德斯基的声明不是“纯粹主义”; 离得很远。

在Scala中,没有任何运行时数据不是对象,并且没有什么可以调用不是对象的方法。 在Java中,这些陈述都不是真的; Java是一种部分面向对象的语言。 在Java中,有些东西不是对象,有些方法不在对象上。

Scala的新手经常认为object Foo是Java静态的一些奇怪的替代品,但这是你需要快速通过的东西。 相反,将Java的静态方法视为非OO wart,将Scala的object Foo { ... }视为以下内容:

 class SomeHiddenClass { ... } val Foo = new SomeHiddenClass // the only instance of it 

这里Foo是一个 ,而不是一个类型 ,它确实是一个对象。 它可以传递给一个方法。 它可以扩展其他一些类。 例如:

 abstract class AbFoo { def f:Int } object Foo extends AbFoo { def f = 2 } 

现在,最后,你可以说

 g(Foo) 

确实,类的“伴随对象”是为类放置非实例方法和数据的好地方。 但是该伴随对象是一个对象,因此通常的规则和function适用。

事实上,在Java中,您将这些方法放在非对象上 – 限制它们的使用方式 – 是一种责任,而不是一种function。 当然不是 OO。

我不确定我是否完全接受了这个论点,但这是一个可能的推理。

对于面向对象的纯粹主义者,一切都应该是一个对象,并且所有状态都应该被对象封装。 类的任何static成员都是定义状态,它存在于对象之外 ,因为您可以使用它并在不实例化对象的情况下对其进行操作。 因此,缺少静态类成员会产生更纯粹的面向对象语言。

好吧,对于像方法这样的静态成员,你没有任何可以创建的对象,但是你可以调用这样的静态方法。 您只需要静态类名来设置这些方法的命名空间,例如:

 long timeNow = System.currentTimeMillis(); // no object creation 

这相当于在程序语言中给人一种感觉。

静态成员属于类而不属于对象,而oop的主要概念位于dirrefer类的各个对象之间的关系中。

Java中的static方法是对类本身进行操作的方法,并且不需要首先创建Object。 例如,这一行:

 int c = Integer.parseInt("5"); 

Integer.parseInt()是静态的,因为我没有去Integer i = new Integer(); 在使用之前; 这不是在我创建的任何特定对象上运行,因为它总是相同的,更像是典型的过程函数调用而不是面向对象的方法。 如果我必须为每个调用创建一个对象,并且我们以这种方式封装所有内容而不是允许static使用方法作为虚假过程函数,那么它更加面向对象。

对于确切的面向对象意味着什么有几个相互竞争的定义。 但是,有一件事他们都可以达成一致:动态调度是OO定义的基本部分。

静态方法是静态的(duh),而不是动态的,因此根据定义它们不是面向对象的。

从逻辑上讲,具有非面向对象特征的语言在某种意义上比没有所述特征的语言“少OO”。