问题
As I understand it, when they finally come along, we will be able to substitute a Java closure for the equivalent single-method interface. Is there a standard Scala idiom for doing the same - implementing a Java Single Abstract Method interface with a Scala closure?
Ideally I'd like the following to automagically work
test("Closure") {
var event: PropertyChangeEvent = null
var label = new JLabel()
label.addPropertyChangeListener( {e: PropertyChangeEvent => event = e} )
label.setText("fred")
event.getNewValue should be ("fred")
}
回答1:
There was a lengthy discussion about this in January.
http://www.scala-lang.org/node/8744
The idea was well received, and even taken a bit further than this proposal. But the devil is in the details, and a prototype implementation may yet find other problems with this proposal.
回答2:
In scala, you can try implicit conversions:
implicit def func2PropertyChangeListener( f: ProperyChangeEvent => Unit ) =
new PropertyChangeListener {
def propertyChange( evt: PropertyChangeEvent ) = f(evt)
}
Then you just have to import the method and you can directly pass anonymous functions everywhere a PropertyChangeListener is expected.
回答3:
See http://www.tikalk.com/incubator/blog/simulating-sam-closures-scala
Use implicit conversion to add something like addSamListener.
You'd need to write the conversion, but you wouldn't need to import it everywhere (just the one implicit conversion that adds addSamListener).
If all interface share the same method, you can use structural typing.
回答4:
The only way to do this for every old Java interface is code generation. Since Java parsers are readily available and you only need to extract the interface name and method signature this should be fairly easy to do and it only needs to run once. In fact, it sounds like a good sunday evening project. Other people might benefit from it too...
回答5:
The answer depends on exactly what you're asking...
If calling into Scala code that expects an A => B, then this de-sugars to Function1[A,B] which is already a SAM type, so no further work is needed once Java finally gains closures.
(at least we have the distraction of Duke Nukem Forever, which arrived on our monitors first...)
If Scala is calling Java code that requires a SAM type, then one technique is to provide an implicit conversion from the Scala function to the expected interface (as suggested by paradigmatic). It's also highly likely that a future version of Scala will handle this automatically (as pointed out by retronym)
Also worth mentioning is Scala's specialisation, which can yield functions with multiple overloaded versions. So far as I'm aware, Java won't be providing anything of this nature, so interop would likely have to be via one of the AbstractFunctionN types.
回答6:
I can't quite reproduce my desired syntax, but I may be getting close by using a method with type parameters of the desired type and those of arguments to the block:
test("PropertyChangeListener") {
var event: PropertyChangeEvent = null
val label = new JLabel()
label.addPropertyChangeListener(SAM[PropertyChangeListener, PropertyChangeEvent] {
event = _
})
label.setText("fred")
event.getNewValue should be ("fred")
}
test("Runnable") {
var sideEffect: Any = null
val r: Runnable = SAM[Runnable] { sideEffect = "fred" }
r.run
sideEffect should be ("fred")
}
test("Callable") {
val c: Callable[String] = SAM[Callable[String]] { "fred" }
c.call should be ("fred")
}
def SAM[T](block: => Any)(implicit classManifest: ClassManifest[T]): T = {
java.lang.reflect.Proxy.newProxyInstance(
getClass.getClassLoader,
Array(classManifest.erasure),
new InvocationHandler {
def invoke(proxy: Object, method: Method, args:Array[AnyRef]) = {
var result = block
block.asInstanceOf[AnyRef]
}
}).asInstanceOf[T]
}
def SAM[T, A](block: Function1[A,Any])(implicit classManifest: ClassManifest[T]): T = {
java.lang.reflect.Proxy.newProxyInstance(
getClass.getClassLoader,
Array(classManifest.erasure),
new InvocationHandler {
def invoke(proxy: Object, method: Method, args:Array[AnyRef]) = {
var result = block(args(0).asInstanceOf[A])
result.asInstanceOf[AnyRef]
}
}).asInstanceOf[T]
}
来源:https://stackoverflow.com/questions/6483262/generically-implementing-a-java-single-abstract-method-interface-with-a-scala-cl