Как получить имена и типы параметров через отражение в методах Scala / Java?

Мы можем использовать отражение, чтобы получить имена методов следующим образом:

object Foo { def bar(name:String, age:Int) = {} } 
val foo = Foo.getClass
val methods = foo.getMethods.filter(_.getName.startsWith("b"))
methods.foreach(m => println(m.getName))

Теперь мне нужно получить типы и имена параметров.

  • Сохраняются ли имена параметров в байт-коде? Если да, как получить к ним доступ?
  • Если ответ выше отрицательный, можем ли мы как-то сохранить имена с помощью аннотаций?
  • Может кто-нибудь привел пример, чтобы прочитать типы и способы их использования. Меня интересуют только функции, имеющие параметры типа String и / или Array[String].

[EDIT:] Java-версия решения тоже в порядке.

[EDIT:] Аннотации кажутся одним из способов сделать это. Однако поддержка аннотаций Scala не так хороша. Связанный вопрос SO.


person Jus12    schedule 03.10.2011    source источник
comment
На (1) я считаю, что ответ отрицательный, потому что файлы классов Java не хранят имена. (2) кажется вероятным, но я не знаю. Что касается (3), помните, что на самом деле не весь тип, а только конструктор типа сохраняется из-за стирания. Опять же, аннотации могут решить эту проблему, но, может быть, было бы лучше попробовать систему, которая не использует отражение? По моему опыту, хотя иногда рефлексия может быть удобной, вы в конечном итоге пожалеете об этом (а сейчас это даже не кажется удобным).   -  person Owen    schedule 04.10.2011
comment
Кажется, что имена хранятся в байт-коде, потому что, когда я использую jar-файл Scala из Java (без исходного кода Scala), завершение кода IDE Eclipse / Netbeans показывает имена.   -  person Jus12    schedule 04.10.2011
comment
@ Jus12 Это не является требованием к байт-коду и, вероятно, связано с наличием отладочных символов. javap -l распечатает таблицу символов, если таковая существует; вы можете проверить разницу, протестировав как java, так и java -g:vars, чтобы увидеть файл класса с информацией об имени символа и без нее.   -  person Dave Newton    schedule 04.10.2011
comment
Я создал его по другим причинам, но он также охватывает вопрос: gist.github.com/1257784 Это должно Следует отметить, что он работает только с основной версией компилятора Scala.   -  person Grzegorz Kossakowski    schedule 04.10.2011


Ответы (3)


Я не пробовал, но http://paranamer.codehaus.org/ предназначен для этой задачи.

person Duncan McGregor    schedule 03.10.2011
comment
Мне нравится этот! Это все самодостаточно, независимо от asm; копирует с него только нужную часть. - person lyomi; 08.12.2014

Спецификация байт-кода Java не требует сохранения имен параметров. Однако они могут проникнуть через отладочные символы (если компилятору было приказано их сгенерировать). Я знаю, что библиотека байт-кода ASM считывает эти символы, если они есть. См. мой ответ на вопрос «Как чтобы получить имена параметров конструкторов объекта » для примера Java поиска имен параметров конструктора (в байт-коде конструкторы - это просто методы с именем <init>).

person Adam Paynter    schedule 03.10.2011
comment
Спасибо. Я изменил ваш ответ, чтобы он работал со Scala. Просто нужно выяснить, как обращаться с классом Type. - person Jus12; 06.10.2011
comment
@ Jus12: Какие у тебя проблемы с классом Type? - person Adam Paynter; 06.10.2011
comment
Это была моя ошибка. Я использовал Java Type, тогда как вместо этого я должен был использовать класс библиотеки Type. Я решил это. - person Jus12; 06.10.2011

Если в классах присутствует отладочная информация, это можно сделать следующим образом.

Я в основном использую Ответ Адама Пэйнтера и скопируйте и вставьте код из здесь после небольшого редактирования, чтобы заставить его работать в Scala.

package test
import java.io.InputStream
import java.util.ArrayList
import scala.collection.JavaConversions._
import org.objectweb.asm.ClassReader
import org.objectweb.asm.Type
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.LocalVariableNode
import org.objectweb.asm.tree.MethodNode

object Util {
  case class Param(paraName:String, paraType:Type)
  case class ScalaMethod(name:String, returnType:Type, params:List[Param])

  def main(args:Array[String]):Unit = {
    getMethods(scala.util.Random.getClass).foreach(printMethod _ )
    def printMethod(m:ScalaMethod) = {
      println (m.name+" => "+m.returnType.getClassName)
      m.params.foreach(p =>
        println (" "+ p.paraName+":"+p.paraType.getClassName))
    }
  }

  /**
   * extracts the names, parameter names and parameter types of all methods of c
   */
  def getMethods(c:Class[_]):List[ScalaMethod] = {
    val cl:ClassLoader = c.getClassLoader();
    val t:Type = Type.getType(c);
    val url:String = t.getInternalName() + ".class";
    val is:InputStream = cl.getResourceAsStream(url);
    if (is == null)
      throw new IllegalArgumentException("""The class loader cannot
                                         find the bytecode that defined the
                                         class (URL: " + url + ")""");
    val cn = new ClassNode();
    val cr = new ClassReader(is);
    cr.accept(cn, 0);
    is.close();
    val methods = cn.methods.asInstanceOf[java.util.List[MethodNode]];
    var mList:List[ScalaMethod] = Nil
    if (methods.size > 0) for (i <- 1 to methods.size) {
      val m:MethodNode = methods.get(i-1)
      val argTypes:Array[Type] = Type.getArgumentTypes(m.desc);
      val paraNames = new java.util.ArrayList[String](argTypes.length)
      val vars = m.localVariables.asInstanceOf[java.util.List[LocalVariableNode]];
      var pList:List[Param] = Nil
      if (argTypes.length > 0) for (i <- 0 to argTypes.length) {
          // The first local variable actually represents the "this" object
          paraNames.add(vars.get(i).name);
          pList = Param(paraNames.get(i-1), argTypes(i-1)) :: pList
      }
      mList = ScalaMethod(m.name, Type.getReturnType(m.desc), pList) :: mList
    }
    mList
  }
}
person Jus12    schedule 06.10.2011