Generically implementing a Java Single-Abstract-Method interface with a Scala closure?

牧云@^-^@ 提交于 2019-12-10 02:48:56

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!