2010-04-18 4 views
2

Могу ли я получить метод через отражение, каким-то образом объединить его с целевым объектом и вернуть его как что-то похожее на функцию в Scala (т. Е. Вы можете вызвать ее с помощью скобок)? Список аргументов является переменным. Это не должно быть «первоклассной» функцией (я обновил вопрос), просто синтаксически выглядящий вызов функции, например. F (арг).Есть ли простой (идиоматический) способ преобразования java.lang.reflect.Method в функцию Scala?

Моя попытка до сих пор выглядит примерно так (что технически это псевдо-код, просто чтобы избежать загромождения пост с дополнительными определениями):

 
class method_ref(o: AnyRef, m: java.lang.reflect.Method) { 
    def apply(args: Any*): some_return_type = { 
     var oa: Array[Object] = args.toArray.map { _.asInstanceOf[Object] } 
     println("calling: " + m.toString + " with: " + oa.length) 

     m.invoke(o, oa: _*) match { 
      case x: some_return_type => x; 
      case u => throw new Exception("unknown result" + u); 
     } 
    } 
} 

С выше, я смог пройти компилятором ошибки, но теперь у меня есть исключение времени выполнения:

 
Caused by: java.lang.IllegalArgumentException: argument type mismatch 

пример использования что-то вроде:

 
    var f = ... some expression returning method_ref ...; 
    ... 
    var y = f(x) // looks like a function, doesn't it? 

UPDATE

Изменение арг: Любой * в аргументах: AnyRef * фактически фиксированная моя проблема времени выполнения, поэтому выше подход (с исправлением) штраф за то, что я пытаюсь выполнить работы. Думаю, я столкнулся с более общей проблемой с варгарами.

+0

Ох ... Я пропустил дело о «легком». Нет, нет. Раньше, но он был экспериментальным и был отброшен. –

+0

Вы всегда можете объяснить _where_, у вас есть ошибка, так как метод_ref, который вы отправили, не будет компилироваться. –

+0

На самом деле это похоже на изменение Any * на AnyRef * исправлено мою проблему, хотя я не понимаю, почему. –

ответ

2

Если вы не ищете общий invoke, который принимает имя метода, но скорее вы хотите захватить конкретный метод для определенного объекта - и вы не хотите слишком глубоко проникать в манифесты и Таким образом, я думаю, что следующее порядочное решение:

class MethodFunc[T <: AnyRef](o: Object, m: reflect.Method, tc: Class[T]) { 
    def apply(oa: Any*): T = { 
    val result = m.invoke(o, oa.map(_.asInstanceOf[AnyRef]): _*) 
    if (result.getClass == tc) result.asInstanceOf[T] 
    else throw new IllegalArgumentException("Unexpected result " + result) 
    } 
} 

Давайте посмотрим его в действии:

val s = "Hi there, friend" 
val m = s.getClass.getMethods.find(m => { 
    m.getName == "substring" && m.getParameterTypes.length == 2 
}).get 
val mf = new MethodFunc(s,m,classOf[String]) 

scala> mf(3,8) 
res10: String = there 

хитрая часть получает правильный тип для возвращаемого значения. Здесь вам остается предоставить его. Например, если вы укажете classOf[CharSequence], он не будет работать, потому что это не тот класс. (Манифесты лучше для этого, но вы просили простую ... хотя я думаю, что «простой в использовании», как правило, лучше, чем «просто кодировать функциональность».)

+0

В моем случае тип возврата фиксирован, так что лишний бит сложности не нужен. Меня все еще озадачивает материал varargs. Я вижу, что вы бросили «_.asInstanceOf [AnyRef]« где у меня »_.asInstanceOf [Object]». –

+0

@Alex: 'AnyRef' - это Scala для' Object'. Я использую 'Object' для обозначения« это Java-y вещь здесь »и« AnyRef »для обозначения« это Scala-y thing there ». –

4

Несомненно. Вот код, который я написал, чтобы добавить интерфейс к функции. Это не совсем то, что вы хотите, но я думаю, что его можно адаптировать с небольшими изменениями. Самое сложное изменение - на invoke, где вам нужно будет изменить вызываемый метод на тот, который получен через отражение. Кроме того, вам нужно будет позаботиться о том, чтобы полученный вами метод был apply. Кроме того, вместо f вы должны использовать целевой объект. Это, вероятно, следует искать что-то вроде этого:

def invoke(proxy: AnyRef, method: Method, args: Array[AnyRef]) = method match { 
    case m if /* m is apply */ => target.getClass().getMethod("name", /* parameter type */).invoke(target, args: _*) 
    case _ => /* ??? */ 
    } 

Во всяком случае, вот код:

import java.lang.reflect.{Proxy, InvocationHandler, Method} 

class Handler[T, R](f: Function1[T, R])(implicit fm: Manifest[Function1[T, R]]) extends InvocationHandler { 
    def invoke(proxy: AnyRef, method: Method, args: Array[AnyRef]) = method.invoke(f, args: _*) 
    def withInterface[I](implicit m: Manifest[I]) = { 
    require(m <:< manifest[Function1[T, R]] && m.erasure.isInterface) 
    Proxy.newProxyInstance(m.erasure.getClassLoader(), Array(m.erasure), this).asInstanceOf[I] 
    } 
} 

object Handler { 
    def apply[T, R](f: Function1[T, R])(implicit fm: Manifest[Function1[T, R]]) = new Handler(f) 
} 

И использовать его как это:

trait CostFunction extends Function1[String, Int] 
Handler { x: String => x.length } withInterface manifest[CostFunction] 

Использование "проявляются" там помогает синтаксис. Вы можете написать следующее:

Handler({ x: String => x.length }).withInterface[CostFunction] // or 
Handler((_: String).length).withInterface[CostFunction] 

Можно также отказаться от манифеста и вместо этого использовать classOf с несколькими изменениями.

+0

Yikes. Там должно быть что-то более простое. Пожалуйста, ознакомьтесь с моим обновленным вопросом с тем, что я пробовал до сих пор, которое на данный момент компилируется, но не выполняется во время выполнения. –

 Смежные вопросы

  • Нет связанных вопросов^_^