scala :::
FP侧
在Scala中将方法转换为函数很容易。 例如,如果您有一个方法:
def say(to: Person, what: String): String = { ... }
我们可以使用下划线符号获取相应的功能:
val sayFun: (Person, String) => String = say _
而且,Scala支持多个参数列表,有时也称为currying ,这使得部分应用更加容易:
// Method with multiple parameter list
def connect(person1: Person)(person2: person): Connection = { ... }
// Function, created by partially applying the previous method
val createConnectionWithAdam: Person => Connection =
connect(new Person('Adam')) _
面向对象的一面
每个类都有一件事是构造函数。 但是什么是构造函数呢? 您可以为构造函数的参数指定值,然后获取该类的新实例。 因此,它实际上只是一个功能!
class Person(name: String, age: Int) { ... }
// The 'signature' of new is (String, Int) => Person
val somebody = new Person('John', 34)
链接?
但是,这就是OO和FP组合在Scala中失败的地方:不能将上述方法的两个功能(转换为函数,currying)与构造函数一起使用。 这两个都不起作用 :
val makePerson: (String, Int) => Person = new Person _
class Person2(name: String)(age: Int) { ... }
val makeNewJack: Int => Person = new Person2('Jack') _
您可以使用伴随对象解决此问题并apply
(或任何其他工厂方法,应用之后会有一个更好的表示法):
object Person2 {
def apply(name: String)(age: Int) = new Person2(name, age)
}
val makeNewJack: Int => Person = Person2('Jack') _
但这需要在同伴对象中重复构造函数的签名,没有人喜欢重复代码,对吗? ;)
用例
这在哪里有用? 例如在经典工厂示例中。 假设您有一个类,它依赖于某些服务,但也依赖于运行时可用的某些数据。 当然,我们使用IoC,因此向我们的班级提供了其他服务的实例:
// This service depends on a concrete Person instance
class Service3(service1: Service1, service2: Service2)(person: Person) {
...
}
/ Note that the multiple parameter notation above als provides a nice
// separation of the parameters that should be 'injected' - services,
// and the data that can be variable.
// This service depends on Service1, and wants to create it having Person
// instances
class Service4(makeService3: Person => Service3) {
// Usage:
for (person <- persons) makeService3(person).doSomething()
}
class Main {
// Bootstrap: (or - no-framework DI container ;) )
val service1 = new Service1
val service2 = new Service2
// That's the part that is illegal in Scala
val makeService3 = new Service3(service1, service2) _
val service4 = new Service4(makeService1)
...
// Today we'd have to write: val makeService3 = (person: Person) => new Service3(//service1, service2, person)
}
这也与我在DI和OO上的文章有关,以及当前的DI框架如何难以定义依赖于我们希望拥有多个副本的数据和服务的服务。
边注
当将构造函数视为方法/函数时(实际上是;)),我假设使用类似Ruby的符号:
Person.new('John', 34)
会更好,并且增加对_和多个参数列表的支持将是显而易见的。
TL; DR风扇的底线
为什么不将类构造函数与其他所有方法/函数一样对待? 使其合法:
class Person(name: String, age: Int)
val makePerson = new Person _ // type: (String, Int) => Person
class Person2(name: String)(age: Int)
val makeNewJack = new Person2('Jack') _ // type: Int => Person
参考: 在我们的JCG合作伙伴 Adam Warski的博客中, 缺少Scala中的OO和FP桥接器 。
翻译自: https://www.javacodegeeks.com/2012/08/scala-missing-oo-and-fp-bridge.html
scala :::