Wenn ich eine Klasse wie diese habe:
public class Whatever
{
public void aMethod(int aParam);
}
Gibt es eine Möglichkeit zu wissen, dass aMethod
ein Parameter namens verwendet aParam
wird, der vom Typ ist int
?
Wenn ich eine Klasse wie diese habe:
public class Whatever
{
public void aMethod(int aParam);
}
Gibt es eine Möglichkeit zu wissen, dass aMethod
ein Parameter namens verwendet aParam
wird, der vom Typ ist int
?
Antworten:
Zusammenfassen:
method.getParameterTypes()
Um die Autovervollständigungsfunktion für einen Editor zu schreiben (wie Sie in einem der Kommentare angegeben haben), gibt es einige Optionen:
arg0
, arg1
, arg2
usw.intParam
, stringParam
, objectTypeParam
etc.In Java 8 können Sie Folgendes tun:
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(){}
}
Für Ihre Klasse können Whatever
wir also einen manuellen Test durchführen:
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;
}
}
}
}
Dies sollte gedruckt werden, [aParam]
wenn Sie ein -parameters
Argument an Ihren Java 8-Compiler übergeben haben.
Für Maven-Benutzer:
<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>
Weitere Informationen finden Sie unter folgenden Links:
Die Paranamer-Bibliothek wurde erstellt, um dasselbe Problem zu lösen.
Es wird versucht, Methodennamen auf verschiedene Arten zu ermitteln. Wenn die Klasse mit Debugging kompiliert wurde, kann sie die Informationen durch Lesen des Bytecodes der Klasse extrahieren.
Eine andere Möglichkeit besteht darin, ein privates statisches Element in den Bytecode der Klasse einzufügen, nachdem es kompiliert wurde, aber bevor es in ein Glas gestellt wird. Anschließend werden diese Informationen mithilfe von Reflection zur Laufzeit aus der Klasse extrahiert.
https://github.com/paul-hammant/paranamer
Ich hatte Probleme mit dieser Bibliothek, aber am Ende habe ich sie zum Laufen gebracht. Ich hoffe, die Probleme dem Betreuer melden zu können.
ParameterNAmesNotFoundException
Siehe org.springframework.core.DefaultParameterNameDiscoverer-Klasse
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));
Ja.
Der Code muss mit einem Java 8-kompatiblen Compiler kompiliert werden, mit der Option zum Speichern der aktivierten formalen Parameternamen ( Option -parameters ).
Dann sollte dieses Code-Snippet funktionieren:
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());
}
}
Sie können die Methode mit Reflektion abrufen und ihre Argumenttypen erkennen. Überprüfen Sie http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html#getParameterTypes%28%29
Sie können jedoch den Namen des verwendeten Arguments nicht angeben.
Es ist möglich und Spring MVC 3 macht es, aber ich habe mir nicht die Zeit genommen, genau zu sehen, wie.
Der Abgleich von Methodenparameternamen mit URI-Vorlagenvariablennamen ist nur möglich, wenn Ihr Code mit aktiviertem Debugging kompiliert wurde. Wenn Sie das Debuggen nicht aktiviert haben, müssen Sie den Namen des Variablennamens der URI-Vorlage in der Annotation @PathVariable angeben, um den aufgelösten Wert des Variablennamens an einen Methodenparameter zu binden. Beispielsweise:
Entnommen aus der Federdokumentation
Während es nicht möglich ist (wie andere gezeigt haben), Sie könnten eine Anmerkung verwenden , um die Parameter - Namen zu übertragen, und dass , obwohl Reflexion erhalten.
Nicht die sauberste Lösung, aber sie erledigt den Job. Einige Webservices tun dies tatsächlich, um Parameternamen beizubehalten (dh: Bereitstellen von WSs mit Glassfish).
Siehe java.beans.ConstructorProperties , eine Anmerkung, die genau dafür entwickelt wurde.
Sie sollten also in der Lage sein:
Whatever.declaredMethods
.find { it.name == 'aMethod' }
.parameters
.collect { "$it.type : $it.name" }
Aber Sie werden wahrscheinlich eine Liste wie diese erhalten:
["int : arg0"]
Ich glaube, dass dies in Groovy 2.5+ behoben wird
Derzeit lautet die Antwort also:
Siehe auch:
Für jede Methode dann so etwas wie:
Whatever.declaredMethods
.findAll { !it.synthetic }
.collect { method ->
println method
method.name + " -> " + method.parameters.collect { "[$it.type : $it.name]" }.join(';')
}
.each {
println it
}
aMethod
. Ich möchte es für alle Methoden in einer Klasse bekommen.
antlr
, um Parameternamen dafür zu erhalten?
Wie @Bozho sagte, ist dies möglich, wenn Debug-Informationen während der Kompilierung enthalten sind. Hier gibt es eine gute Antwort ...
Wie erhalte ich die Parameternamen der Konstruktoren eines Objekts (Reflektion)? von @AdamPaynter
... mit der ASM-Bibliothek. Ich habe ein Beispiel zusammengestellt, das zeigt, wie Sie Ihr Ziel erreichen können.
Beginnen Sie zunächst mit einer pom.xml mit diesen Abhängigkeiten.
<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>
Dann sollte diese Klasse tun, was Sie wollen. Rufen Sie einfach die statische Methode auf 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;
}
}
Hier ist ein Beispiel mit einem 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) {
}
}
}
Das vollständige Beispiel finden Sie auf GitHub
static
Methoden. Dies liegt daran, dass in diesem Fall die Anzahl der von ASM zurückgegebenen Argumente unterschiedlich ist, dies jedoch leicht behoben werden kann.Parameternamen sind nur für den Compiler nützlich. Wenn der Compiler eine Klassendatei generiert, werden die Parameternamen nicht berücksichtigt. Die Argumentliste einer Methode besteht nur aus der Anzahl und den Typen ihrer Argumente. Es ist also unmöglich, den Parameternamen mithilfe von Reflection (wie in Ihrer Frage markiert) abzurufen - er existiert nirgendwo.
Wenn die Verwendung von Reflektion jedoch keine schwierige Anforderung ist, können Sie diese Informationen direkt aus dem Quellcode abrufen (vorausgesetzt, Sie haben sie).
Parameter names are only useful to the compiler.
Falsch. Schauen Sie sich die Nachrüstbibliothek an. Es verwendet dynamische Schnittstellen, um REST-API-Anforderungen zu erstellen. Eine seiner Funktionen ist die Möglichkeit, Platzhalternamen in den URL-Pfaden zu definieren und diese Platzhalter durch die entsprechenden Parameternamen zu ersetzen.
Um meine 2 Cent hinzuzufügen; Parameterinformationen sind in einer Klassendatei "zum Debuggen" verfügbar, wenn Sie javac -g zum Kompilieren der Quelle verwenden. Und es steht APT zur Verfügung, aber Sie benötigen eine Anmerkung, die für Sie keinen Nutzen hat. (Jemand hat vor 4-5 Jahren hier etwas Ähnliches besprochen: http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0 )
Kurz gesagt, Sie können es nur erhalten, wenn Sie direkt an Quelldateien arbeiten (ähnlich wie bei APT zur Kompilierungszeit).