问候,
这是一个具有挑衅性的问题,旨在就如何在开发者社区中看到抽象反转进行辩论。我真的很想知道你的想法。
首先,这里引用维基百科给出的抽象反转例子: http://en.wikipedia.org/wiki/Abstraction_inversion
在面向对象的语言(如Java和C ++)中创建表示函数的对象是很麻烦的,其中函数不是第一类对象。在C ++中,可以通过重载()运算符来使对象“可调用”,但实际上仍然需要实现一个新类,例如STL中的Functors。
对于我来说,函数是Scala中的一等公民,但是当我们使用Scala生成Java字节码时,Scala会在Java上创建特定的类'以使函数式编程成为可能... 我们可以将其视为抽象反转?
同样适用于Clojure或JVM的任何功能语言......甚至Apache Collections,例如:
http://commons.apache.org/collections/apidocs/org/apache/commons/collections/Closure.html
答案 0 :(得分:9)
维基文章真的很弱(它代表了一个抽象反转本身吗?:),这个概念有点可疑。但它的基本要点似乎是抽象隐藏了一些基本元素,迫使该抽象的用户重新实现它。
例如,从谈话页面来看,这是一个更有趣的例子。假设CPU具有tan
数学函数,但没有sin
或cos
,并且sin
和cos
的编程语言实现tan
},但没有暴露tan
本身。使用该语言的程序员将被迫以tan
和sin
来实现cos
,tan
和{{1}}本身是以{{1}}实现的,因此表征抽象反转。
所以,回到Scala。当然,在Scala中使用函数的程序员不会引发抽象反转,因为他没有被迫重新实现作为Scala的原语可用的功能。
另一方面,有人可能会声称Scala将函数实现为类实例是抽象反转的一个实例。然而,要做到这一点,有两件事情也必须成立:
究竟什么是功能?函数原语会是什么样的?在此上下文中,“功能”表示能够执行的数据。有人可能会说,所有汇编程序代码实际上都是能够执行的数据 - 只是它不可移植,而且不再是字节码,因此不符合设计原则。
另一方面,Java中的所有方法都由标识符引用,Java通过该标识符定位要为给定对象的类层次结构执行的代码。虽然可以通过反射间接使用,但不会公开此标识符。如果它被公开,并且提供了一些功能来说“调用这段代码”,那么可以说可以构建一个函数。
所以,我认为可以为1做一个案例。让我们继续下一步。
如果Java 提供“方法”数据类型,Scala函数是否会停止成为类的实例?不,他们不会。 Scala的一个基本设计方面是正在运行的程序的每个元素都是一个对象。 Java已经具有的“原语”就好像它们是带有方法的普通对象一样,如果有一个“方法”原语,那么它就会出现。
方法原语的一个可能后果是将方法提升为一等公民,但功能本身几乎不会改变。
答案 1 :(得分:5)
通过对象实现一个函数并不是因为这就是JVM上的可能,它切入了Scala的基本哲学。
一切是一个对象:函数,参与者,数字文字等。任何对象都可以应用(通过定义apply()
方法),而不是实际的子类功能N
这种二元性在许多标准库API的设计中是基础,它允许例如映射既可以作为函数(映射键到值),也可以作为对象(键/值对的集合)。
Scala是一个真正的对象/功能混合体,两种范式都没有占主导地位。
答案 2 :(得分:2)
不,它不能被视为抽象反转。原因很简单:在Scala中,您有一个选择您更喜欢的抽象级别。大多数时候写
更方便val f = 2 * (_:Int)
//--> f: (Int) => Int =
f(21)
//--> res5: Int = 42
但以“长”的方式做到这一点没有问题:
val f = new Function1[Int,Int] { def apply(v:Int) = 2*v }
//--> f: java.lang.Object with (Int) => Int =
f(21)
//--> res6: Int = 42
由于FunctionN是特征,因此可以使用混合继承,这可以避免像C ++中的Functors那样的情况。例如。你可以“改进”Java-Map作为一个函数:
class JavaMap[K,V] extends java.util.HashMap[K,V] with Function1[K,V] {
def apply(k:K) = get(k)
}
val m = new JavaMap[Int, String]
m.put(5,"five")
m(5)
//--> res8: String = five
答案 3 :(得分:1)
我不相信。在Scala中实现第一类函数的方式就是实现细节。