다음과 같은 수업이있는 경우 :
public class Whatever
{
public void aMethod(int aParam);
}
유형 aMethod
의 매개 변수 를 사용하는 것을 알 수있는 방법이 있습니까?aParam
int
다음과 같은 수업이있는 경우 :
public class Whatever
{
public void aMethod(int aParam);
}
유형 aMethod
의 매개 변수 를 사용하는 것을 알 수있는 방법이 있습니까?aParam
int
답변:
요약:
method.getParameterTypes()
(주석 중 하나에서 언급했듯이) 편집기의 자동 완성 기능을 작성하기 위해 몇 가지 옵션이 있습니다.
arg0
, arg1
, arg2
등intParam
, stringParam
, objectTypeParam
, 등Java 8에서는 다음을 수행 할 수 있습니다.
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(){}
}
따라서 수업에 Whatever
대해 수동 테스트를 수행 할 수 있습니다.
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;
}
}
}
}
Java 8 컴파일러에 인수를 [aParam]
전달한 경우 인쇄 해야합니다 -parameters
.
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>
자세한 내용은 다음 링크를 참조하십시오.
이 같은 문제를 해결하기 위해 Paranamer 라이브러리가 만들어졌습니다.
몇 가지 다른 방법으로 메서드 이름을 결정하려고합니다. 클래스가 디버깅으로 컴파일 된 경우 클래스의 바이트 코드를 읽어 정보를 추출 할 수 있습니다.
또 다른 방법은 컴파일 된 후 jar에 배치되기 전에 전용 정적 멤버를 클래스의 바이트 코드에 삽입하는 것입니다. 그런 다음 리플렉션을 사용하여 런타임에 클래스에서이 정보를 추출합니다.
https://github.com/paul-hammant/paranamer
이 라이브러리를 사용하는 데 문제가 있었지만 결국 작동하게되었습니다. 관리자에게 문제를보고하고 싶습니다.
ParameterNAmesNotFoundException
org.springframework.core.DefaultParameterNameDiscoverer 클래스 참조
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));
예. 형식 매개 변수 이름을 저장하는 옵션이 설정된 Java 8 호환 컴파일러 로
코드 를 컴파일해야합니다 ( -parameters 옵션).
그러면이 코드 조각이 작동합니다.
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());
}
}
리플렉션을 사용하여 메서드를 검색하고 인수 유형을 감지 할 수 있습니다. http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html#getParameterTypes%28%29 확인
그러나 사용 된 인수의 이름은 말할 수 없습니다.
가능하고 Spring MVC 3가 그것을 수행하지만 정확히 어떻게하는지 시간이 걸리지 않았습니다.
메서드 매개 변수 이름과 URI 템플릿 변수 이름의 일치는 디버깅이 활성화 된 상태로 코드가 컴파일 된 경우에만 수행 할 수 있습니다. 디버깅을 활성화하지 않은 경우 변수 이름의 확인 된 값을 메서드 매개 변수에 바인딩하려면 @PathVariable 주석에 URI 템플릿 변수 이름의 이름을 지정해야합니다. 예를 들면 :
그것이 (다른 사람이 설명대로) 수는 없지만, 당신은 수있는 매개 변수 이름을 이월 주석을 사용하고 반사하지만 그것을 얻을 수 있습니다.
가장 깨끗한 솔루션은 아니지만 작업을 완료합니다. 일부 웹 서비스는 실제로 매개 변수 이름을 유지하기 위해이 작업을 수행합니다 (예 : glassfish로 WS 배포).
java.beans.ConstructorProperties를 참조하십시오 . 정확히 이것을 수행하도록 설계된 주석입니다.
따라서 다음을 수행 할 수 있어야합니다.
Whatever.declaredMethods
.find { it.name == 'aMethod' }
.parameters
.collect { "$it.type : $it.name" }
그러나 아마도 다음과 같은 목록을 얻을 것입니다.
["int : arg0"]
나는 이것이 Groovy 2.5+에서 수정 될 것이라고 믿습니다.
현재 답은 다음과 같습니다.
또한보십시오:
모든 방법에 대해 다음과 같습니다.
Whatever.declaredMethods
.findAll { !it.synthetic }
.collect { method ->
println method
method.name + " -> " + method.parameters.collect { "[$it.type : $it.name]" }.join(';')
}
.each {
println it
}
aMethod
. 클래스의 모든 메서드에 대해 가져오고 싶습니다.
antlr
이것에 대한 매개 변수 이름을 얻는 데 사용할 수 없습니까?
@Bozho가 말했듯이 컴파일 중에 디버그 정보가 포함되어 있으면 가능합니다. 여기에 좋은 대답이 있습니다 ...
객체 생성자의 매개 변수 이름을 얻는 방법 (반사)? 작성자 @AdamPaynter
... ASM 라이브러리 사용. 목표를 달성 할 수있는 방법을 보여주는 예를 모았습니다.
우선, 이러한 종속성이있는 pom.xml로 시작하십시오.
<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>
그런 다음이 수업은 원하는 것을해야합니다. 정적 메서드를 호출하기 만하면 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;
}
}
다음은 단위 테스트의 예입니다.
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) {
}
}
}
GitHub 에서 전체 예제를 찾을 수 있습니다.
static
메소드에서 작동하지 않습니다 . 이 경우 ASM에서 반환하는 인수의 수가 다르지만 쉽게 수정할 수 있기 때문입니다.매개 변수 이름은 컴파일러에만 유용합니다. 컴파일러가 클래스 파일을 생성 할 때 매개 변수 이름은 포함되지 않습니다. 메소드의 인수 목록은 인수의 수와 유형으로 만 구성됩니다. 따라서 리플렉션을 사용하여 매개 변수 이름을 검색하는 것은 불가능합니다 (질문에 태그가 지정됨)-어디에도 존재하지 않습니다.
그러나 리플렉션 사용이 어려운 요구 사항이 아닌 경우 소스 코드에서이 정보를 직접 검색 할 수 있습니다 (있다고 가정).
Parameter names are only useful to the compiler.
잘못된. Retrofit 라이브러리를보십시오. 동적 인터페이스를 사용하여 REST API 요청을 생성합니다. 그 기능 중 하나는 URL 경로에 자리 표시 자 이름을 정의하고 해당 자리 표시자를 해당 매개 변수 이름으로 바꾸는 기능입니다.
2 센트를 추가하려면 매개 변수 정보는 javac -g를 사용하여 소스를 컴파일 할 때 "디버깅 용"클래스 파일에서 사용할 수 있습니다. APT에서 사용할 수 있지만 주석이 필요하므로 쓸모가 없습니다. (누군가 4 ~ 5 년 전에 비슷한 것을 논의했습니다. http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0 )
요약하자면 소스 파일에서 직접 작업하지 않으면 얻을 수 없습니다 (APT가 컴파일 타임에 수행하는 작업과 유사 함).