Как я могу сохранить ссылку на набор статических методов Java из Scala? - PullRequest
2 голосов
/ 07 февраля 2011

В контексте привязки данных Eclipse существует довольно много Java-классов, которые действуют как фабрики для IObservable объектов. Например, есть BeanObservables, PojoObservables, EMFObservables и т. Д., И все они реализуют набор методов, подобных

  public static IObservableValue observeValue(Object target, String property)

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

Но так как они определены в Java, похоже, я ничего не могу сделать. Я не могу написать

val pojoObservableFact = PojoObservables

потому что PojoObservables не распознается как значение.

Есть ли способ заставить компилятор "создавать" экземпляр-синглтон, который бы соответствовал классу PojoObservables в Scala? Или какой-нибудь обходной путь для этого? Я знаю, что мог бы сам написать синглтон и переслать все вызовы методов, но это звучит немного утомительно.


Редактировать

Чтобы было понятнее: вот что я пытаюсь сделать: определить метод, который создает наблюдаемое значение, используя переданную фабрику.

def createObservable(fact: ObservableFactory, target: AnyRef, property: String) =
  fact.observeValue(target, property)

но я не знаю, как определить ObservableFactory или как на самом деле передать PojoObservables, класс Java только со статическими методами, в этот createObservable метод.

Ответы [ 4 ]

4 голосов
/ 07 февраля 2011

Если никто не придет с рабочим ответом, я не думаю, что было бы так сложно написать генератор кода, основанный на сигнатурах метода.

val a = classOf[String].getMethods.map(_.toString).filter(s =>
  s.contains("public") && s.contains("static")
)

будет, например,вытащите все сигнатуры метода (для String в данном случае) в виде строк.Тогда

val b = a.map(_.split(" ").reverse.take(2).reverse)

получит только сигнатуру функции с типом возврата, разделенным спереди.Теперь

val c = b.map(_(1).dropWhile(_ != '(').drop(1).takeWhile(_ != ')').split(","))
val d = b.map(_(1).takeWhile(_ != '('))

получит подписи аргумента и имя функции соответственно.Затем нам нужно преобразовать примитивные типы, что довольно просто, поскольку нам просто нужно использовать заглавную букву в первой букве (за исключением void, который становится единицей):

def jprim2scala(s: String) = {
  val prim = List("boolean","byte","short","char","int","long","float","double")
  def arrayconvert(s: String): String = {
    if (s.endsWith("[]")) "Array["+arrayconvert(s.substring(0,s.length-2))+"]"
    else if (s=="void") "Unit"
    else if (prim contains s) {
      s.substring(0,1).toUpperCase + s.substring(1)
    }
    else s
  }
  arrayconvert(s)
}

def e = (b,c).zipped.map((bi,ci) => (jprim2scala(bi(0)), ci.map(jprim2scala)))

и, наконец, вы можете собрать все это вместе:

val f = (d,e).zipped.map((name,ei) => {
  val (ret,args) = ei
  val lastname = name.split('.').last
  "def "+lastname+"(" +
  (for ((a,i) <- args.zipWithIndex) yield ("a"+i+": "+a)).mkString(", ") +
  "): "+ret+" = "+name+"("+(0 until args.length).map("a"+_).mkString(",")+")"
})

Теперь у вас есть набор кода Scala, который вы можете поместить (вручную) в соответствующий синглтон.

object Stringleton {
  // The contents of this is cut-and-paste f.map("  "+_).foreach(println)
  def valueOf(a0: java.lang.Object): java.lang.String = java.lang.String.valueOf(a0)
  def valueOf(a0: Array[Char]): java.lang.String = java.lang.String.valueOf(a0)
  def valueOf(a0: Array[Char], a1: Int, a2: Int): java.lang.String = java.lang.String.valueOf(a0,a1,a2)
  def valueOf(a0: Boolean): java.lang.String = java.lang.String.valueOf(a0)
  def valueOf(a0: Char): java.lang.String = java.lang.String.valueOf(a0)
  def valueOf(a0: Int): java.lang.String = java.lang.String.valueOf(a0)
  def valueOf(a0: Long): java.lang.String = java.lang.String.valueOf(a0)
  def valueOf(a0: Float): java.lang.String = java.lang.String.valueOf(a0)
  def valueOf(a0: Double): java.lang.String = java.lang.String.valueOf(a0)
  def copyValueOf(a0: Array[Char], a1: Int, a2: Int): java.lang.String = java.lang.String.copyValueOf(a0,a1,a2)
  def copyValueOf(a0: Array[Char]): java.lang.String = java.lang.String.copyValueOf(a0)
  def format(a0: java.lang.String, a1: Array[java.lang.Object]): java.lang.String = java.lang.String.format(a0,a1)
  def format(a0: java.util.Locale, a1: java.lang.String, a2: Array[java.lang.Object]): java.lang.String = java.lang.String.format(a0,a1,a2)
}

(Вараргс, кстати, здесь не работает -вам нужно исправить их, если они присутствуют. Вы можете сделать это, вернувшись к методу .isVarArgs из самого метода (не его строкового представления) и преобразовав последний массив [X] в X *, а затем вызвав егоas ai: _*.)

Хорошая особенность этого подхода в том, что если есть общие методы, вам даже не нужно использовать структурную типизацию для их извлечения - вы можете определить свою собственную чертучто ваши упакованные методы наследуют!(И если вы особенно амбициозны, вы можете использовать почти подходящие методы, чтобы соответствовать друг другу, например, чтобы все имели одно и то же имя метода.)

3 голосов
/ 07 февраля 2011

Почему бы просто не передать фабричный метод (или набор методов, если необходимо)?

def createObservable(fact: (AnyRef, String) => IObservableValue, 
target: AnyRef, property: String) = fact(target, property)

createObservable(BeanObservables.createObservable, target, property)
0 голосов
/ 07 февраля 2011

Итак, взяв ссылку для PojoObservables из здесь , я вижу статический метод observeValue, как вы описываете.

Это очень просто: Scala обрабатывает статические методы в классах Java так, как если бы они были определены для компаньона. Вы можете использовать это, как если бы это была старая школа Java:

import org.eclipse.core.databinding.beans.PojoObservables
...
val ov = PojoObservables.observeValue(target, property)

Или вариант новой школы со статическим импортом:

import org.eclipse.core.databinding.beans.PojoObservables._
...
val ov = observeValue(target, property)

Затем вы можете передать любой такой метод как функциональный объект. Идея, что функция должна каким-то образом сопровождаться своим интерфейсом, очень Java-ориентированное мышление , просто ссылайтесь на все, что вам нужно, и предоставьте в качестве аргумента другие функции более высокого класса:)

0 голосов
/ 07 февраля 2011

Одним из способов является использование отражения:

object PojoObservables

val pojoObservableFact = PojoObservables.getClass.getConstructor()
pojoObservableFact.setAccessible(true)
pojoObservableFact.newInstance()

Теперь мы можем вызывать методы в PojoObservables.method ...

...