Jika saya memiliki kelas seperti ini:
public class Whatever
{
public void aMethod(int aParam);
}
apakah ada cara untuk mengetahui yang aMethod
menggunakan parameter bernama aParam
, yaitu tipe int
?
Jika saya memiliki kelas seperti ini:
public class Whatever
{
public void aMethod(int aParam);
}
apakah ada cara untuk mengetahui yang aMethod
menggunakan parameter bernama aParam
, yaitu tipe int
?
Jawaban:
Untuk meringkas:
method.getParameterTypes()
Demi menulis fungsionalitas pelengkapan otomatis untuk editor (seperti yang Anda nyatakan di salah satu komentar) ada beberapa opsi:
arg0
, arg1
, arg2
dllintParam
, stringParam
, objectTypeParam
, dllDi Java 8 Anda dapat melakukan hal berikut:
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
public final class Methods {
public static List<String> getParameterNames(Method method) {
Parameter[] parameters = method.getParameters();
List<String> parameterNames = new ArrayList<>();
for (Parameter parameter : parameters) {
if(!parameter.isNamePresent()) {
throw new IllegalArgumentException("Parameter names are not present!");
}
String parameterName = parameter.getName();
parameterNames.add(parameterName);
}
return parameterNames;
}
private Methods(){}
}
Jadi untuk kelas Anda, Whatever
kami dapat melakukan tes manual:
import java.lang.reflect.Method;
public class ManualTest {
public static void main(String[] args) {
Method[] declaredMethods = Whatever.class.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.getName().equals("aMethod")) {
System.out.println(Methods.getParameterNames(declaredMethod));
break;
}
}
}
}
yang akan dicetak [aParam]
jika Anda telah memberikan -parameters
argumen ke compiler Java 8 Anda.
Untuk pengguna Maven:
<properties>
<!-- PLUGIN VERSIONS -->
<maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
<!-- OTHER PROPERTIES -->
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<!-- Original answer -->
<compilerArgument>-parameters</compilerArgument>
<!-- Or, if you use the plugin version >= 3.6.2 -->
<parameters>true</parameters>
<testCompilerArgument>-parameters</testCompilerArgument>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
Untuk informasi lebih lanjut lihat tautan berikut:
Perpustakaan Paranamer dibuat untuk mengatasi masalah yang sama ini.
Ia mencoba untuk menentukan nama metode dengan beberapa cara berbeda. Jika kelas dikompilasi dengan debugging, ia dapat mengekstrak informasi dengan membaca bytecode kelas.
Cara lain adalah dengan menginjeksi anggota statis pribadi ke dalam bytecode kelas setelah dikompilasi, tetapi sebelum ditempatkan di dalam toples. Kemudian menggunakan refleksi untuk mengekstrak informasi ini dari kelas pada saat runtime.
https://github.com/paul-hammant/paranamer
Saya mengalami masalah saat menggunakan perpustakaan ini, tetapi pada akhirnya saya berhasil membuatnya berfungsi. Saya berharap untuk melaporkan masalah ini ke pengelola.
ParameterNAmesNotFoundException
lihat kelas org.springframework.core.DefaultParameterNameDiscoverer
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));
Iya.
Kode harus dikompilasi dengan compiler yang sesuai dengan Java 8 dengan opsi untuk menyimpan nama parameter formal yang diaktifkan ( opsi -parameters ).
Maka cuplikan kode ini akan berfungsi:
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());
}
}
Anda dapat mengambil metode dengan refleksi dan mendeteksi tipe argumennya. Periksa http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html#getParameterTypes%28%29
Namun, Anda tidak dapat memberi tahu nama argumen yang digunakan.
Itu mungkin dan Spring MVC 3 melakukannya, tetapi saya tidak meluangkan waktu untuk melihat persis bagaimana caranya.
Pencocokan nama parameter metode ke nama variabel Template URI hanya dapat dilakukan jika kode Anda dikompilasi dengan debugging diaktifkan. Jika Anda belum mengaktifkan debugging, Anda harus menentukan nama nama variabel Template URI dalam anotasi @PathVariable untuk mengikat nilai yang diselesaikan dari nama variabel ke parameter metode. Sebagai contoh:
Diambil dari dokumentasi musim semi
Meskipun tidak mungkin (seperti yang digambarkan orang lain), Anda bisa menggunakan anotasi untuk membawa nama parameter, dan mendapatkannya melalui refleksi.
Bukan solusi terbersih, tetapi menyelesaikan pekerjaan. Beberapa layanan web sebenarnya melakukan ini untuk menyimpan nama parameter (yaitu: menyebarkan WS dengan glassfish).
Lihat java.beans.ConstructorProperties , ini adalah anotasi yang dirancang untuk melakukan hal ini.
Jadi, Anda harus bisa melakukan:
Whatever.declaredMethods
.find { it.name == 'aMethod' }
.parameters
.collect { "$it.type : $it.name" }
Tetapi Anda mungkin akan mendapatkan daftar seperti ini:
["int : arg0"]
Saya yakin ini akan diperbaiki di Groovy 2.5+
Jadi saat ini jawabannya adalah:
Lihat juga:
Untuk setiap metode, maka sesuatu seperti:
Whatever.declaredMethods
.findAll { !it.synthetic }
.collect { method ->
println method
method.name + " -> " + method.parameters.collect { "[$it.type : $it.name]" }.join(';')
}
.each {
println it
}
aMethod
. Saya ingin mendapatkannya untuk semua metode di kelas.
antlr
untuk mendapatkan nama parameter untuk ini?
Seperti yang dinyatakan @Bozho, dimungkinkan untuk melakukannya jika informasi debug disertakan selama kompilasi. Ada jawaban yang bagus di sini ...
Bagaimana cara mendapatkan nama parameter konstruktor objek (refleksi)? oleh @AdamPaynter
... menggunakan perpustakaan ASM. Saya mengumpulkan contoh yang menunjukkan bagaimana Anda dapat mencapai tujuan Anda.
Pertama-tama, mulailah dengan pom.xml dengan dependensi ini.
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
<version>5.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
Kemudian, kelas ini harus melakukan apa yang Anda inginkan. Panggil saja metode statis getParameterNames()
.
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;
public class ArgumentReflection {
/**
* Returns a list containing one parameter name for each argument accepted
* by the given constructor. If the class was compiled with debugging
* symbols, the parameter names will match those provided in the Java source
* code. Otherwise, a generic "arg" parameter name is generated ("arg0" for
* the first argument, "arg1" for the second...).
*
* This method relies on the constructor's class loader to locate the
* bytecode resource that defined its class.
*
* @param theMethod
* @return
* @throws IOException
*/
public static List<String> getParameterNames(Method theMethod) throws IOException {
Class<?> declaringClass = theMethod.getDeclaringClass();
ClassLoader declaringClassLoader = declaringClass.getClassLoader();
Type declaringType = Type.getType(declaringClass);
String constructorDescriptor = Type.getMethodDescriptor(theMethod);
String url = declaringType.getInternalName() + ".class";
InputStream classFileInputStream = declaringClassLoader.getResourceAsStream(url);
if (classFileInputStream == null) {
throw new IllegalArgumentException(
"The constructor's class loader cannot find the bytecode that defined the constructor's class (URL: "
+ url + ")");
}
ClassNode classNode;
try {
classNode = new ClassNode();
ClassReader classReader = new ClassReader(classFileInputStream);
classReader.accept(classNode, 0);
} finally {
classFileInputStream.close();
}
@SuppressWarnings("unchecked")
List<MethodNode> methods = classNode.methods;
for (MethodNode method : methods) {
if (method.name.equals(theMethod.getName()) && method.desc.equals(constructorDescriptor)) {
Type[] argumentTypes = Type.getArgumentTypes(method.desc);
List<String> parameterNames = new ArrayList<String>(argumentTypes.length);
@SuppressWarnings("unchecked")
List<LocalVariableNode> localVariables = method.localVariables;
for (int i = 1; i <= argumentTypes.length; i++) {
// The first local variable actually represents the "this"
// object if the method is not static!
parameterNames.add(localVariables.get(i).name);
}
return parameterNames;
}
}
return null;
}
}
Berikut adalah contoh dengan unit test.
public class ArgumentReflectionTest {
@Test
public void shouldExtractTheNamesOfTheParameters3() throws NoSuchMethodException, SecurityException, IOException {
List<String> parameterNames = ArgumentReflection
.getParameterNames(Clazz.class.getMethod("callMe", String.class, String.class));
assertEquals("firstName", parameterNames.get(0));
assertEquals("lastName", parameterNames.get(1));
assertEquals(2, parameterNames.size());
}
public static final class Clazz {
public void callMe(String firstName, String lastName) {
}
}
}
Anda dapat menemukan contoh lengkapnya di GitHub
static
metode. Ini karena dalam hal ini jumlah argumen yang dikembalikan oleh ASM berbeda, tetapi itu adalah sesuatu yang dapat diperbaiki dengan mudah.Nama parameter hanya berguna untuk kompilator. Ketika kompilator membuat file kelas, nama parameter tidak disertakan - daftar argumen metode hanya terdiri dari jumlah dan jenis argumennya. Jadi tidak mungkin untuk mengambil nama parameter menggunakan refleksi (seperti yang ditandai dalam pertanyaan Anda) - tidak ada di mana pun.
Namun, jika penggunaan refleksi bukanlah persyaratan yang sulit, Anda dapat mengambil informasi ini langsung dari kode sumber (dengan asumsi Anda memilikinya).
Parameter names are only useful to the compiler.
Salah. Lihatlah perpustakaan Retrofit. Ini menggunakan Antarmuka dinamis untuk membuat permintaan REST API. Salah satu fiturnya adalah kemampuan untuk menentukan nama placeholder di jalur URL dan mengganti placeholder tersebut dengan nama parameter yang sesuai.
Untuk menambahkan 2 sen saya; info parameter tersedia dalam file kelas "untuk debugging" saat Anda menggunakan javac -g untuk mengompilasi sumber. Dan itu tersedia untuk APT tetapi Anda memerlukan anotasi sehingga tidak berguna bagi Anda. (Seseorang mendiskusikan hal serupa 4-5 tahun yang lalu di sini: http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0 )
Secara keseluruhan singkatnya Anda tidak bisa mendapatkannya kecuali Anda mengerjakan file Sumber secara langsung (mirip dengan apa yang dilakukan APT pada waktu kompilasi).