Nếu tôi có một lớp học như thế này:
public class Whatever
{
public void aMethod(int aParam);
}
có cách nào để biết rằng aMethod
sử dụng một tham số có tên aParam
, đó là loại int
không?
Nếu tôi có một lớp học như thế này:
public class Whatever
{
public void aMethod(int aParam);
}
có cách nào để biết rằng aMethod
sử dụng một tham số có tên aParam
, đó là loại int
không?
Câu trả lời:
Tóm lại:
method.getParameterTypes()
Vì lợi ích của việc viết chức năng tự động hoàn thành cho trình chỉnh sửa (như bạn đã nêu trong một trong các nhận xét), có một số tùy chọn:
arg0
, arg1
,arg2
, vvintParam
, stringParam
,objectTypeParam
vvTrong Java 8, bạn có thể làm như sau:
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(){}
}
Vì vậy, đối với lớp học của bạn, Whatever
chúng tôi có thể thực hiện một bài kiểm tra thủ công:
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;
}
}
}
}
cái nào sẽ in [aParam]
nếu bạn đã vượt qua-parameters
đối số vào trình biên dịch Java 8 của mình.
Đối với người dùng 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>
Để biết thêm thông tin, hãy xem các liên kết sau:
Thư viện Paranamer được tạo ra để giải quyết vấn đề tương tự.
Nó cố gắng xác định tên phương thức theo một vài cách khác nhau. Nếu lớp được biên dịch bằng cách gỡ lỗi, nó có thể trích xuất thông tin bằng cách đọc mã bytecode của lớp.
Một cách khác là nó đưa một thành viên tĩnh riêng tư vào bytecode của lớp sau khi nó được biên dịch, nhưng trước khi nó được đặt trong một jar. Sau đó, nó sử dụng phản chiếu để trích xuất thông tin này từ lớp trong thời gian chạy.
https://github.com/paul-hammant/paranamer
Tôi đã gặp sự cố khi sử dụng thư viện này, nhưng cuối cùng tôi đã làm cho nó hoạt động. Tôi hy vọng sẽ báo cáo sự cố cho người bảo trì.
ParameterNAmesNotFoundException
xem lớp org.springframework.core.DefaultParameterNameDiscoverer
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));
Đúng.
Mã phải được biên dịch bằng trình biên dịch tuân thủ Java 8 với tùy chọn lưu trữ tên tham số chính thức được bật ( tùy chọn -parameters ).
Sau đó, đoạn mã này sẽ hoạt động:
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());
}
}
Bạn có thể truy xuất phương thức với sự phản chiếu và phát hiện các loại đối số của nó. Kiểm tra http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html#getParameterTypes%28%29
Tuy nhiên, bạn không thể biết tên của đối số được sử dụng.
Điều đó là có thể và Spring MVC 3 đã làm được điều đó, nhưng tôi không dành thời gian để xem chính xác cách thức.
Việc đối sánh tên tham số phương thức với tên biến Mẫu URI chỉ có thể được thực hiện nếu mã của bạn được biên dịch với tính năng gỡ lỗi được bật. Nếu bạn chưa bật gỡ lỗi, bạn phải chỉ định tên của tên biến Mẫu URI trong chú thích @PathVariable để liên kết giá trị đã phân giải của tên biến với tham số phương thức. Ví dụ:
Lấy từ tài liệu mùa xuân
Mặc dù không thể (như những người khác đã minh họa), bạn có thể sử dụng chú thích để chuyển tên tham số và nhận được thông số đó mặc dù phản ánh.
Không phải là giải pháp sạch nhất, nhưng nó sẽ hoàn thành công việc. Một số dịch vụ web thực sự làm điều này để giữ tên tham số (ví dụ: triển khai WS với cá thủy tinh).
Xem java.beans.ConstructorProperties , đó là một chú thích được thiết kế để thực hiện chính xác điều này.
Vì vậy, bạn sẽ có thể làm:
Whatever.declaredMethods
.find { it.name == 'aMethod' }
.parameters
.collect { "$it.type : $it.name" }
Nhưng có thể bạn sẽ nhận được một danh sách như vậy:
["int : arg0"]
Tôi tin rằng điều này sẽ được khắc phục trong Groovy 2.5+
Vì vậy, hiện tại, câu trả lời là:
Xem thêm:
Đối với mọi phương pháp, sau đó một cái gì đó như:
Whatever.declaredMethods
.findAll { !it.synthetic }
.collect { method ->
println method
method.name + " -> " + method.parameters.collect { "[$it.type : $it.name]" }.join(';')
}
.each {
println it
}
aMethod
. Tôi muốn lấy nó cho tất cả các phương thức trong một lớp.
antlr
để lấy tên tham số cho điều này?
Như @Bozho đã nêu, bạn có thể thực hiện điều đó nếu thông tin gỡ lỗi được đưa vào trong quá trình biên dịch. Có một câu trả lời hay ở đây ...
Làm cách nào để lấy tên tham số của các hàm tạo của một đối tượng (phản chiếu)? bởi @AdamPaynter
... sử dụng thư viện ASM. Tôi đưa ra một ví dụ cho thấy cách bạn có thể đạt được mục tiêu của mình.
Trước hết, hãy bắt đầu với một pom.xml với những phụ thuộc này.
<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>
Sau đó, lớp này sẽ làm những gì bạn muốn. Chỉ cần gọi phương thức tĩnh 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;
}
}
Đây là một ví dụ với bài kiểm tra đơn vị.
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) {
}
}
}
Bạn có thể tìm thấy ví dụ đầy đủ trên GitHub
static
các phương pháp. Đây là điều cần thiết trong trường hợp này, số lượng các đối số được trả về bởi ASM là khác nhau, nhưng nó là thứ có thể dễ dàng sửa chữa.Tên tham số chỉ hữu ích cho trình biên dịch. Khi trình biên dịch tạo một tệp lớp, tên tham số không được bao gồm - danh sách đối số của một phương thức chỉ bao gồm số lượng và kiểu đối số của nó. Vì vậy, sẽ không thể truy xuất tên tham số bằng cách sử dụng phản chiếu (như được gắn thẻ trong câu hỏi của bạn) - nó không tồn tại ở bất kỳ đâu.
Tuy nhiên, nếu việc sử dụng phản xạ không phải là một yêu cầu khó, bạn có thể lấy thông tin này trực tiếp từ mã nguồn (giả sử bạn có nó).
Parameter names are only useful to the compiler.
Sai lầm. Nhìn vào thư viện Retrofit. Nó sử dụng các Giao diện động để tạo các yêu cầu API REST. Một trong những tính năng của nó là khả năng xác định tên trình giữ chỗ trong đường dẫn URL và thay thế những trình giữ chỗ đó bằng tên tham số tương ứng của chúng.
Để thêm 2 xu của tôi; thông tin tham số có sẵn trong tệp lớp "để gỡ lỗi" khi bạn sử dụng javac -g để biên dịch nguồn. Và nó có sẵn cho APT nhưng bạn sẽ cần một chú thích để không sử dụng cho bạn. (Ai đó đã thảo luận điều gì đó tương tự cách đây 4-5 năm tại đây: http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0 )
Nói tóm lại, bạn không thể lấy nó trừ khi bạn làm việc trực tiếp trên các tệp Nguồn (tương tự như những gì APT thực hiện tại thời điểm biên dịch).