Java 8には、Reflection APIを使用してメソッドパラメーター名を取得する機能があります。
これらのメソッドパラメータ名を取得するにはどうすればよいですか?
私の知る限り、クラスファイルには正式なパラメータ名は格納されていません。リフレクションを使用してこれらを取得するにはどうすればよいですか?
これらのメソッドパラメータ名を取得するにはどうすればよいですか?
基本的に、次のことが必要です。
Class
への参照を取得しますClass
から、 getDeclaredMethod()
またはgetDeclaredMethods()
を呼び出してMethod
オブジェクトへの参照を返すことにより、Method
への参照を取得します。Method
オブジェクトから、Parameter
オブジェクトの配列を返す(Java 8)の新しい)getParameters()
を呼び出しますParameter
オブジェクトで、getName()
を呼び出しますClass<String> clz = String.class;
for (Method m : clz.getDeclaredMethods()) {
System.err.println(m.getName());
for (Parameter p : m.getParameters()) {
System.err.println(" " + p.getName());
}
}
出力:
...
indexOf
arg0
indexOf
arg0
arg1
...
また、私の知る限りでは、.classファイルには仮パラメータが格納されていません。次に、リフレクションを使用してそれらを取得するにはどうすればよいですか?
Parameter.getName()
のjavadocを参照してください:
...パラメータの名前が存在する場合、このメソッドはクラスファイルによって提供される名前を返します。 それ以外の場合、このメソッドはargNの形式の名前を合成します。Nは、パラメーターを宣言するメソッドの記述子内のパラメーターのインデックスです。 。
JDKがこれをサポートするかどうかは実装固有です(上記の出力からわかるように、JDK 8のビルド125はサポートしていません)。クラスファイル形式は、特定のJVM/javac実装で使用でき、それをサポートしない他の実装では無視されるオプションの属性をサポートします。
上記の出力は、arg0
、arg1
、...、pre Java 8 JVMs-知っている必要があるのは、Method.getParameterTypes()
を介してアクセス可能なパラメータ数だけです。 :
Class<String> clz = String.class;
for (Method m : clz.getDeclaredMethods()) {
System.err.println(m.getName());
int paramCount = m.getParameterTypes().length;
for (int i = 0; i < paramCount; i++) {
System.err.println(" arg" + i);
}
}
JDK 8の新しい点は、拡張APIがあり、JVMがarg0
、arg1
の代わりに実際のパラメーター名を提供するpossibilityであるということです。 ...
このようなオプション機能のサポートは、optional attributesを通じて可能です。これは、さまざまなクラスファイル構造に添付できます。クラスファイル内のmethod_info
構造体については、 4.6。メソッド を参照してください。 JVM仕様の 4.7.1。新しい属性の定義と命名 も参照してください。
JDK 8では、クラスファイルのバージョンが52に増加するため、この機能をサポートするためにファイル形式自体を変更することもできます。
JEP 118:実行時のパラメーター名へのアクセス も参照してください。提案されている実装モデルは、パラメータ名を格納するオプションの属性を追加することです。クラスファイル形式はすでにこれらのオプション属性をサポートしているため、クラスファイルが仕様で要求されているように単純に無視される古いJVMで引き続き使用できるように、これも可能です。
Java Virtual Machineの実装は、認識しない属性を静かに無視する必要があります。
更新
@assyliasで示唆されているように、パラメータ名を反映するためのメタデータをクラスファイルに追加するには、ソースをjavac
コマンドラインオプション-parameters
でコンパイルする必要があります。ただし、これはもちろんこのオプションでコンパイルされたコードにのみ影響します-ランタイムライブラリはこのフラグでコンパイルされず、したがってクラスファイルに必要なエントリが含まれていないため、上記のコードはarg0
、arg1
などを出力します。
Andreasに感謝しますが、最終的に Method Parameters のOracleチュートリアルから完全なソリューションを得ました。
それは言います、
Java.lang.reflect.Executable.getParametersメソッドを使用して、任意のメソッドまたはコンストラクターの仮パラメーターの名前を取得できます。 (クラスMethodおよびConstructorは、クラスExecutableを拡張するため、メソッドExecutable.getParametersを継承します。)ただし、.classファイルは、デフォルトでは正式なパラメーター名を格納しません。これは、クラスファイルを生成および使用する多くのツールが、パラメータ名を含む.classファイルのより大きな静的および動的なフットプリントを期待しないためです。特に、これらのツールはより大きな.classファイルを処理する必要があり、Java仮想マシン(JVM)はより多くのメモリを使用します。さらに、シークレットやパスワードなどの一部のパラメーター名は、セキュリティに敏感なメソッドに関する情報を公開します。
特定の.classファイルに仮パラメータ名を保存し、Reflection APIが仮パラメータ名を取得できるようにするには、javacコンパイラの-parametersオプションを使用してソースファイルをコンパイルします。
Remember to compile the with the -parameters compiler option
Java MethodParameterSpy ExampleMethods
このコマンドは次を出力します。
Number of constructors: 1
Constructor #1
public ExampleMethods()
Number of declared constructors: 1
Declared constructor #1
public ExampleMethods()
Number of methods: 4
Method #1
public boolean ExampleMethods.simpleMethod(Java.lang.String,int)
Return type: boolean
Generic return type: boolean
Parameter class: class Java.lang.String
Parameter name: stringParam
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Parameter class: int
Parameter name: intParam
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Method #2
public int ExampleMethods.varArgsMethod(Java.lang.String...)
Return type: int
Generic return type: int
Parameter class: class [Ljava.lang.String;
Parameter name: manyStrings
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Method #3
public boolean ExampleMethods.methodWithList(Java.util.List<Java.lang.String>)
Return type: boolean
Generic return type: boolean
Parameter class: interface Java.util.List
Parameter name: listParam
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Method #4
public <T> void ExampleMethods.genericMethod(T[],Java.util.Collection<T>)
Return type: void
Generic return type: void
Parameter class: class [Ljava.lang.Object;
Parameter name: a
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Parameter class: interface Java.util.Collection
Parameter name: c
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
intellij 13のメソッドパラメーターに関する情報(リフレクション経由で使用可能)を保存する 、Eclipse内の「javac -parameters」に相当するIDEは、メソッドに関する情報を保存するパラメータ(リフレクション経由で使用可能) 'ウィンドウ->設定-> Java->コンパイラ。
Paranamer libを使用できます( https://github.com/paul-hammant/paranamer )
私のために働くサンプルコード:
import com.thoughtworks.paranamer.AnnotationParanamer;
import com.thoughtworks.paranamer.BytecodeReadingParanamer;
import com.thoughtworks.paranamer.CachingParanamer;
import com.thoughtworks.paranamer.Paranamer;
Paranamer info = new CachingParanamer(new AnnotationParanamer(new BytecodeReadingParanamer()));
Method method = Foo.class.getMethod(...);
String[] parameterNames = info.lookupParameterNames(method);
Mavenを使用する場合、pom.xmlにこの依存関係を追加します。
<dependency>
<groupId>com.thoughtworks.paranamer</groupId>
<artifactId>paranamer</artifactId>
<version>2.8</version>
</dependency>