我正在阅读《Programming Scala》。在第四章开头,作者评论说Java支持静态方法,这是“不太纯粹的面向对象概念”。为什么会这样?
我正在阅读《Programming Scala》。在第四章开头,作者评论说Java支持静态方法,这是“不太纯粹的面向对象概念”。为什么会这样?
面向对象编程涵盖三个方面:
在这三个方面中,最重要的是消息传递。
“消息传递”的概念意味着,在面向对象编程中,计算通过自包含对象网络发送消息进行。发送消息是通信/计算的唯一方式。
静态方法违反了至少消息传递和后期绑定。
静态方法并不是与任何对象相关联的方法。根据通常定义,它们实际上并不是方法,而只是过程。 Java静态方法Foo.bar
和BASIC子例程FOO_BAR
几乎没有任何区别。
至于后期绑定:更现代的称呼是动态分派。静态方法也违反了这一原则,事实上,这甚至可以从它们的名称中看出来:“静态”方法。
静态方法打破了面向对象编程的一些非常好的属性。例如,面向对象系统自动具备对象作为能力的功能安全性。静态方法(或者实际上是任何静态方法和静态状态)破坏了这种属性。
您还可以在自己的进程中并行执行每个对象,因为它们只通过消息进行通信,从而提供了一些简单的并发性。 (基本上就像Actors一样,这应该不会太惊讶,因为Carl Hewitt基于Smalltalk-71创建了Actor模型,而Alan Kay则部分基于PLANNER创建了Smalltalk-71,而PLANNER又是由Carl Hewitt创建的。演员和对象之间的密切关系远非偶然,事实上,它们本质上是相同的。)再次强调,静态方法(包括静态方法和尤其是静态状态)破坏了这种良好的属性。
Math.atan2方法。它需要两个数字并且不需要任何状态。您甚至可以将其作为方法的对象进行制作吗?在“纯” OO语言中, Math 本身可能是一个对象(可能是单例),而 atan2 将是实例方法,但由于函数实际上没有使用 Math 对象中的任何状态,因此它也不是“纯OO”概念。
atan2
函数用于测量一个单点到正x轴的弧度角。换句话说,它一开始不应该接受两个数字参数,而应该接受一个单点参数。用面向对象的术语来看,它可以被建模为一个点对象上的方法。 - Jörg W Mittagstdev
和average
函数怎么处理?它们应该是Array
的方法吗?还是它们应该是自己的类(Stats
)的实例方法,该类保存所有数字,以便您必须将您的Array
转换为Stats
才能对它们求平均值?或者它们应该是静态方法,你只需要传递一个数字数组给它们? - Gabe目前为止还没有提到静态方法不是很面向对象的一个原因是接口和抽象类只定义非静态方法。因此,静态方法不太适合继承。
请注意,静态方法没有访问“super
”的权限,这意味着静态方法无法实际上被重写。实际上,它们根本无法被重写,只能被隐藏。尝试一下:
public class Test {
public static int returnValue() {
return 0;
}
public static void main(String[] arg) {
System.out.println(Test.returnValue());
System.out.println(Test2.returnValue());
Test x = new Test2();
System.out.println(x.returnValue());
}
}
public class Test2 extends Test {
public static int returnValue() {
return 1;
}
}
当你运行这段代码时,你得不到你所期望的结果。 Test.returnValue()
返回你期望的内容,而 Test2.returnValue()
则隐藏了超类中同名方法(它并没有覆盖它),并返回你期望的内容。静态方法会导致紧密耦合,这违反了良好的面向对象设计原则。调用代码和静态方法内部的代码之间的紧密耦合无法通过依赖反转来避免,因为静态方法本质上不支持面向对象的设计技术,如继承和多态。
此外,由于这些紧密耦合的依赖关系,静态方法往往难以进行测试,这通常会导致代码依赖于第三方基础设施(例如数据库),并且这使得在不实际更改代码的情况下更改行为变得非常困难。
由于以下原因,静态方法不被认为是良好的面向对象编程实践:
1) 防止重用:
静态方法无法重写,并且不能在接口中使用。
2) 对象生命周期很长:
静态方法会在内存中保留很长时间,垃圾回收需要很长时间。开发人员无法控制静态变量的销毁或创建。过度使用静态变量可能导致内存溢出。
3) 此外,还有一些其他要点:
它不尊重封装,因为对象不再完全受其状态的控制。 它不遵循控制反转、松耦合、依赖注入等概念。
静态方法并不是纯粹的面向对象概念,因为它们可以在没有与之关联的对象的情况下被调用。您可以使用类本身。像这样调用它们:Classname.method(...);
面向对象的概念是关于从对象中控制/访问数据,但静态方法不需要使用对象调用,它们属于类而不是对象。
--干杯