Czy Mogę uzyskać nazwę parametru metody używając Java reflection?
Jeśli mam taką klasę:
public class Whatever
{
public void aMethod(int aParam);
}
Czy Jest jakiś sposób, aby wiedzieć, że aMethod
używa parametru o nazwie aParam
, który jest typu int
?
14 answers
Podsumowując:
- uzyskanie nazw parametrów jest możliwe, jeśli podczas kompilacji zawarte są informacje o debugowaniu. Zobacz ta odpowiedź Po Więcej Szczegółów
- w przeciwnym razie uzyskanie nazw parametrów jest nie możliwe
- uzyskanie typu parametru jest możliwe, używając
method.getParameterTypes()
W celu napisania funkcji autouzupełniania dla edytora (jak napisałeś w jednym z komentarzy) jest kilka opcji:
- użyj
arg0
,arg1
,arg2
itd. - użycie
intParam
,stringParam
,objectTypeParam
, itd. - użyj kombinacji powyższych-pierwsza dla typów nie-prymitywnych, a druga dla typów prymitywnych.
- w ogóle nie pokazuj nazw argumentów - tylko ich typy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:39
W Javie 8 możesz wykonać następujące czynności:
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(){}
}
Więc dla twojej klasy Whatever
możemy zrobić test manualny:
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;
}
}
}
}
Który powinien wydrukować [aParam]
, Jeśli przekazałeś argument -parameters
do kompilatora Java 8.
Dla użytkowników 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>
Aby uzyskać więcej informacji, zobacz następujące linki:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-14 20:20:45
Biblioteka Paranamer została stworzona w celu rozwiązania tego samego problemu.
Próbuje określić nazwy metod na kilka różnych sposobów. Jeśli klasa została skompilowana z debugowaniem, może ona wyodrębnić informacje poprzez odczytanie kodu bajtowego klasy.
Innym sposobem jest wstrzyknięcie prywatnego statycznego członka do kodu bajtowego klasy po jej skompilowaniu, ale przed umieszczeniem go w jar. Następnie wykorzystuje reflection, aby wydobyć te informacje z klasy w runtime.
Https://github.com/paul-hammant/paranamer
Miałem problemy z używaniem tej biblioteki, ale w końcu udało mi się ją uruchomić. Mam nadzieję zgłosić problem opiekunowi.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-07 18:46:21
Tak.
Kod musi być skompilowany przy użyciu kompilatora zgodnego z Java 8 z włączoną opcją przechowywania formalnych nazw parametrów (opcja-parameters).
Następnie ten fragment kodu powinien działać:
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());
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-02 08:41:04
Możesz pobrać metodę z odbiciem i wykryć jej typy argumentów. Sprawdź http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html#getParameterTypes%28%29
Nie można jednak podać nazwy użytego argumentu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-10 15:13:48
Zobacz org.springframework.rdzeń.DefaultParameterNameDiscoverer class
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-09 14:12:10
Jest to możliwe i Spring MVC 3 to robi, ale nie poświęciłem czasu, aby dokładnie zobaczyć, jak.
Dopasowanie nazw parametrów metody do nazw Zmiennych Szablonu URI można można to zrobić tylko wtedy, gdy kod jest skompilowany z włączonym debugowaniem. Jeśli masz nie włączone debugowanie, musisz podaj nazwę szablonu URI nazwa zmiennej w zmiennej @PathVariable adnotacja w celu związania rozwiązana wartość nazwy zmiennej do parametr metody. Na przykład:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-11 09:08:57
Chociaż nie jest to możliwe (jak inni ilustrowali), możesz użyć adnotacji, aby przenieść nazwę parametru i uzyskać ją mimo odbicia.
Nie jest to najczystsze rozwiązanie, ale robi swoje. Niektóre serwisy internetowe robią to, aby zachować nazwy parametrów (np.: wdrażanie WSs za pomocą glassfish).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-16 15:13:44
Zobacz java.fasola.ConstructorProperties , jest to adnotacja przeznaczona do tego celu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-15 15:15:51
Więc powinieneś być w stanie zrobić:
Whatever.declaredMethods
.find { it.name == 'aMethod' }
.parameters
.collect { "$it.type : $it.name" }
Ale pewnie dostaniesz taką listę:
["int : arg0"]
Wierzę, że to zostanie naprawione w Groovy 2.5+
Więc obecnie odpowiedź brzmi:
- Jeśli to klasa Groovy, to nie, nie możesz zdobyć nazwy, ale powinieneś być w stanie w przyszłości.
- jeśli jest to klasa Java skompilowana Pod Java 8, powinieneś być w stanie.
Zobacz także:
- http://openjdk.java.net/jeps/118
- https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html
Dla każdej metody, wtedy coś w stylu:
Whatever.declaredMethods
.findAll { !it.synthetic }
.collect { method ->
println method
method.name + " -> " + method.parameters.collect { "[$it.type : $it.name]" }.join(';')
}
.each {
println it
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-03 11:39:57
Jeśli używasz eclipse, zobacz poniższy obrazek, aby umożliwić kompilatorowi przechowywanie informacji o parametrach metody
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-23 12:34:14
Nazwy parametrów są użyteczne tylko dla kompilatora. Gdy kompilator generuje plik klasy, nazwy parametrów nie są uwzględniane - lista argumentów metody składa się tylko z Liczby i typów jej argumentów. Tak więc niemożliwe byłoby odzyskanie nazwy parametru za pomocą reflection ( jak zaznaczono w twoim pytaniu) - nigdzie nie istnieje.
Jeśli jednak użycie reflection nie jest trudnym wymogiem, można pobrać tę informację bezpośrednio z kodu źródłowego (zakładając masz).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-10 15:14:29
Aby dodać moje 2 centy; informacja o parametrze jest dostępna w pliku klasy "do debugowania", gdy używasz javac-g do kompilacji źródła. I jest dostępny dla APT, ale będziesz potrzebował adnotacji, więc nie ma dla Ciebie zastosowania. (Ktoś mówił o czymś podobnym 4-5 lat temu tutaj: http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0 )
Ogólnie rzecz biorąc, nie można go uzyskać, jeśli nie pracujesz bezpośrednio na plikach źródłowych(podobnie jak APT robi w czasie kompilacji).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-10 16:00:33
Jak stwierdził @Bozho, można to zrobić, jeśli podczas kompilacji zawarte są informacje o debugowaniu. Tu jest dobra odpowiedź...
Jak uzyskać nazwy parametrów konstruktorów obiektu (odbicie)? by @ AdamPaynter
...korzystanie z biblioteki ASM. Zestawiłem przykład pokazujący, jak można osiągnąć swój cel.
Po pierwsze, zacznij od pom.xml z tymi zależnościami.
<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>
Więc ta klasa powinna robić, co chcesz. Wystarczy powołać się na metoda statyczna 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;
}
}
Oto przykład z testem jednostkowym.
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) {
}
}
}
Pełny Przykład znajdziesz na GitHub
Caveats
- zmieniłem nieco oryginalne rozwiązanie z @AdamPaynter, aby działało na metody. Jeśli dobrze zrozumiałem, jego rozwiązanie działa tylko z konstruktorami.
- To rozwiązanie nie działa z metodami
static
. Jest to spowodowane tym, że w tym przypadku liczba argumentów zwracanych przez ASM jest różna, ale coś, co można łatwo naprawić.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:54