我一直在考虑Misko Hevery的一篇文章,即 Java 中的静态方法是可测试性的死亡。我不想讨论可测试性问题,而是更多关于静态方法的概念。为什么人们如此讨厌它?
确实,我们没有闭包(但我们有一个有点尴尬的匿名函数)、lambdas 和函数作为第一类对象。在某种程度上,我认为静态方法可以用来模仿函数作为第一类对象。
我一直在考虑Misko Hevery的一篇文章,即 Java 中的静态方法是可测试性的死亡。我不想讨论可测试性问题,而是更多关于静态方法的概念。为什么人们如此讨厌它?
确实,我们没有闭包(但我们有一个有点尴尬的匿名函数)、lambdas 和函数作为第一类对象。在某种程度上,我认为静态方法可以用来模仿函数作为第一类对象。
静态方法使测试变得困难,因为它们不能被替换,就这么简单。
静态方法如何“模仿”作为第一类对象1的功能?可以说,他们在这方面比其他任何事情都要糟糕。您可以通过创建单方法接口将函数“模仿”为第一类对象,实际上 Google 的 Java 集合在许多地方(用于谓词、投影等)都做到了这一点。这不能用静态方法来完成——没有办法(除了反射)来传递“当你想应用一个函数时,使用这个方法。
不,我看不出静态方法在这里有什么帮助。它们不鼓励状态更改(因为唯一可用的状态是全局状态和通过参数传入的任何可变状态),但它们在“作为第一类对象的功能”方面没有帮助。
C# 对此有更好的支持(使用 lambda 表达式和委托),但即使这样也没有想象中的那么普遍。(例如,将其与 F# 进行比较。)
1从 Java 8 开始,方法引用将允许将方法转换为适当的单方法接口的实例,这将使所有这些更加相关。早在 2009 年,那还很遥远……
功能性!=功能,为了记录,我将声称一个方法!=功能......
Java 是一种静态类型的面向对象的语言。Java 也以这种方式保持了相对的纯度,但它与函数式语言相去甚远。
虽然您确实可以使用命令式编程来模仿函数式编程的行为,但您永远无法获得 lambda 演算所需的整洁语法。在某种程度上,如果该语言不支持适当的 lambda 演算,它就不是一种函数式编程语言。
C++ 有函数,但 C++ 也有类。因此,C++ 有两种类型的函数,成员函数和函数。当您说方法时,您的意思是成员函数。因为该方法是在对象的实例上调用的。但是当您说静态方法时,您的意思只是函数(在 C/C++ 意义上)。这只是一个用于引用代码元素的词汇表。并且在Java 代码中不能存在于一个类之外,一个方法会暗示它属于某个类,即类型。
到目前为止,我所说的一切都与函数式编程无关,但我认为你明白了你错的地方。
我建议您查看纯函数式编程语言,例如 Haskell 或 Erlang。因为函数式编程语言通常也没有关闭器。
您声称可以使用静态方法将函数模仿为一流的对象,这对我来说听起来真的很奇怪。它听起来更像是一种动态编程语言,而不是函数式编程。
函数式编程的一个特点是数据的不变性。static
确实暗示您不需要表示状态的对象(实例),所以这不是一个糟糕的开始。但是,您确实在班级级别上有状态,但是您可以使 this final
. 由于(静态)方法根本不是一等函数,你仍然需要像匿名类这样丑陋的结构来处理 Java 中某种风格的函数式编程。
FP 最好用函数式语言完成,因为它对高阶函数、不变性、引用透明性等具有必要的语言支持。
但是,这并不意味着您不能使用 Java 这样的命令式语言以函数式风格进行编程。也可以给出其他示例。不是因为你在用 Java 编程,所以你在做 OOP。goto
您可以使用C++ 等结构化语言对全局数据和非结构化控制流 ( ) 进行编程。我可以用像 Scheme 这样的函数式语言来做 OOP。等等。
Steve McConnell在 Code Complete(也是一个非常流行的 SO 参考资料)中提到了用一种语言编程与用一种语言编程的区别。
所以,简而言之,如果你说“静态方法模仿一流的功能”,我不同意。
If, however, and I think that this was more the point you were trying to get across, you would say that "static methods can help for programming in a functional style in Java", I agree.
我对静态方法的最大反对意见是它们不是多态的,并且它们不是以面向对象的方式使用的,相反,你必须使用类(而不是对象)来访问它们。
如果您只使用静态方法,那么您就是在以程序化、非面向对象的风格进行编程。
但是,我能想到的唯一情况是在引入面向对象之前的第一堂编程课程中。
在 Java 中,您不能将一个函数作为另一个函数的参数。
在函数式语言中,如果你有一个函数
def addOne(i) = i + 1
您可以将其传递给另一个函数,例如将其应用于列表的所有元素。
在 Java 中,与
public static int addOne(int i) { return i + 1; }
没有办法做到这一点。