Общая реализация интерфейса Java Single-Abstract-Method с закрытием Scala? - PullRequest
10 голосов
/ 26 июня 2011

Насколько я понимаю, когда они наконец появятся, мы сможем заменить Java-замыкание эквивалентным интерфейсом с одним методом.Существует ли стандартная идиома Scala для того же - реализация интерфейса Java Single Abstract Method с замыканием Scala?

В идеале хотелось бы, чтобы следующее автоматически работало

test("Closure") {
  var event: PropertyChangeEvent = null
  var label = new JLabel()
  label.addPropertyChangeListener( {e: PropertyChangeEvent => event = e} )
  label.setText("fred")
  event.getNewValue should be ("fred")
}

Ответы [ 6 ]

11 голосов
/ 26 июня 2011

В январе было продолжительное обсуждение этого вопроса.

http://www.scala -lang.org / node / 8744

Идея была хорошо принята, и дажевзял немного дальше, чем это предложение.Но дьявол кроется в деталях, и реализация прототипа может еще найти другие проблемы с этим предложением.

4 голосов
/ 26 июня 2011

В scala вы можете попробовать неявные преобразования:

implicit def func2PropertyChangeListener( f: ProperyChangeEvent => Unit ) =
  new  PropertyChangeListener {
     def propertyChange( evt: PropertyChangeEvent ) = f(evt)
  }

Тогда вам просто нужно импортировать метод, и вы можете напрямую передавать анонимные функции везде, где ожидается PropertyChangeListener.

2 голосов
/ 26 июня 2011

См. http://www.tikalk.com/incubator/blog/simulating-sam-closures-scala

Используйте неявное преобразование, чтобы добавить что-то вроде addSamListener.

Вам нужно написать преобразование, но вам не нужно импортировать его везде (только одно неявное преобразование, которое добавляет addSamListener).

Если все интерфейсы используют один и тот же метод, вы можете использовать структурную типизацию.

1 голос
/ 27 июня 2011

Я не могу полностью воспроизвести желаемый синтаксис, но я могу приблизиться, используя метод с параметрами типа желаемого типа и параметрами аргументов блока:

  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]
  }
1 голос
/ 26 июня 2011

Ответ зависит от того, что именно вы спрашиваете ...

Если вызывается код Scala, который ожидает A => B, то он десахрируется до Function1[A,B], который уже является типом SAM, поэтому дальнейшая работа не требуется, когда Java наконец получает замыкания.

(по крайней мере, мы отвлекаемся от Duke Nukem Forever, который впервые появился на наших мониторах ...)

Если Scala вызывает код Java, для которого требуется тип SAM, то один из методов - обеспечить неявное преобразование из функции Scala в ожидаемый интерфейс (как предлагает парадигма). Также весьма вероятно, что будущая версия Scala будет обрабатывать это автоматически (как указано в retronym)

Также стоит упомянуть специализацию Scala, которая может давать функции с несколькими перегруженными версиями. Насколько мне известно, Java не будет предоставлять ничего подобного, поэтому взаимодействие, вероятно, должно осуществляться одним из типов AbstractFunctionN.

1 голос
/ 26 июня 2011

Единственный способ сделать это для каждого старого интерфейса Java - это генерация кода.Поскольку парсеры Java легко доступны, и вам нужно только извлечь имя интерфейса и сигнатуру метода, это должно быть довольно легко сделать, и его нужно запустить только один раз.На самом деле, это звучит как хороший воскресный вечерний проект.Другие люди могут извлечь из этого пользу ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...