Method
原文地址:http://docs.oracle.com/javase/tutorial/reflect/member/method.html
1.獲得方法類型信息
一個方法的聲明包括方法名,修飾符, 參數, 和返回類型,同時還有一些方法可能拋出的異常. 類 java.lang.reflect.Method
提供了一種方式讓我們可以得到方法的這些信息.
實例程序 MethodSpy
展示了枚舉一個類中所有的方法,並返回指定方法的相關信息.
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import static java.lang.System.out;
public class MethodSpy {
private static final String fmt = "%24s: %s%n";
// for the morbidly curious
<E extends RuntimeException> void genericThrow() throws E {}
public static void main(String... args) {
try {
Class<?> c = Class.forName(args[0]);
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods) {
if (!m.getName().equals(args[1])) {
continue;
}
out.format("%s%n", m.toGenericString());
out.format(fmt, "ReturnType", m.getReturnType());
out.format(fmt, "GenericReturnType", m.getGenericReturnType());
Class<?>[] pType = m.getParameterTypes();
Type[] gpType = m.getGenericParameterTypes();
for (int i = 0; i < pType.length; i++) {
out.format(fmt,"ParameterType", pType[i]);
out.format(fmt,"GenericParameterType", gpType[i]);
}
Class<?>[] xType = m.getExceptionTypes();
Type[] gxType = m.getGenericExceptionTypes();
for (int i = 0; i < xType.length; i++) {
out.format(fmt,"ExceptionType", xType[i]);
out.format(fmt,"GenericExceptionType", gxType[i]);
}
}
// production code should handle these exceptions more gracefully
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
}
實例輸入及其輸出:
$ java MethodSpy java.lang.Class getConstructor
public java.lang.reflect.Constructor<T> java.lang.Class.getConstructor
(java.lang.Class<?>[]) throws java.lang.NoSuchMethodException,
java.lang.SecurityException
ReturnType: class java.lang.reflect.Constructor
GenericReturnType: java.lang.reflect.Constructor<T>
ParameterType: class [Ljava.lang.Class;
GenericParameterType: java.lang.Class<?>[]
ExceptionType: class java.lang.NoSuchMethodException
GenericExceptionType: class java.lang.NoSuchMethodException
ExceptionType: class java.lang.SecurityException
GenericExceptionType: class java.lang.SecurityException
它的實際定義代碼爲:
public Constructor<T> getConstructor(Class<?>... parameterTypes)
第一點需要注意的是.這裏的返回和參數類型都是泛型. Method.getGenericReturnType()
會首先在查詢類中的屬性簽名,如果找到了,那就返回該類型.如果沒有找到,該方法會去調用 Method.getReturnType()
,它的返回值便是定義該泛型時的所使用過的值.
第二點需要注意的是,最後一個參數, parameterType
,是一個可變數量的參數.
它被標識爲一個一維數組.如果要區別這個和一個顯示聲明爲數組的參數的話,可以使用方法 Method.isVarArgs()
.
接下來的示例展示了一個返回值爲泛型的例子:
$ java MethodSpy java.lang.Class cast
public T java.lang.Class.cast(java.lang.Object)
ReturnType: class java.lang.Object
GenericReturnType: T
ParameterType: class java.lang.Object
GenericParameterType: class java.lang.Object
對於返回值是泛型的 Class.cast()
的方法,使用反射之後得到它的返回值是java.lang.Object.
這是因爲在編譯時期所有的跟泛型有關的信息都會被擦除. T的擦除被定義爲CLass:
public final class Class<T> implements ..
最後一個示例程序展示了一個輸出一個多次重載的方法的例子:
$ java MethodSpy java.io.PrintStream format
public java.io.PrintStream java.io.PrintStream.format
(java.util.Locale,java.lang.String,java.lang.Object[])
ReturnType: class java.io.PrintStream
GenericReturnType: class java.io.PrintStream
ParameterType: class java.util.Locale
GenericParameterType: class java.util.Locale
ParameterType: class java.lang.String
GenericParameterType: class java.lang.String
ParameterType: class [Ljava.lang.Object;
GenericParameterType: class [Ljava.lang.Object;
public java.io.PrintStream java.io.PrintStream.format
(java.lang.String,java.lang.Object[])
ReturnType: class java.io.PrintStream
GenericReturnType: class java.io.PrintStream
ParameterType: class java.lang.String
GenericParameterType: class java.lang.String
ParameterType: class [Ljava.lang.Object;
GenericParameterType: class [Ljava.lang.Object;
2.獲得方法的參數名稱
我們可以通過 java.lang.reflect.Executable.getParameters
.方法獲得任何方法和構造函數的形式參數的名稱.(Method類和Constructor類都繼承Executable類,因此他們都繼承了方法Executable.getParameters
.)
然而,默認情況下.class文件並不會存儲形式參數名稱.
如果想要保留形參形成,我們需要編譯源碼使用-parameters 選項.
實例程序 MethodParameterSpy
展示瞭如果獲取一個指定類的所有方法和構造函數的形式參數的名稱.實例同時還展示出一些關於參數的其他信息.
MethodParameterSpy源碼:
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle or the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.lang.reflect.*;
import java.util.function.*;
import static java.lang.System.out;
public class MethodParameterSpy {
private static final String fmt = "%24s: %s%n";
// for the morbidly curious
<E extends RuntimeException> void genericThrow() throws E {}
public static void printClassConstructors(Class c) {
Constructor[] allConstructors = c.getConstructors();
out.format(fmt, "Number of constructors", allConstructors.length);
for (Constructor currentConstructor : allConstructors) {
printConstructor(currentConstructor);
}
Constructor[] allDeclConst = c.getDeclaredConstructors();
out.format(fmt, "Number of declared constructors",
allDeclConst.length);
for (Constructor currentDeclConst : allDeclConst) {
printConstructor(currentDeclConst);
}
}
public static void printClassMethods(Class c) {
Method[] allMethods = c.getDeclaredMethods();
out.format(fmt, "Number of methods", allMethods.length);
for (Method m : allMethods) {
printMethod(m);
}
}
public static void printConstructor(Constructor c) {
out.format("%s%n", c.toGenericString());
Parameter[] params = c.getParameters();
out.format(fmt, "Number of parameters", params.length);
for (int i = 0; i < params.length; i++) {
printParameter(params[i]);
}
}
public static void printMethod(Method m) {
out.format("%s%n", m.toGenericString());
out.format(fmt, "Return type", m.getReturnType());
out.format(fmt, "Generic return type", m.getGenericReturnType());
Parameter[] params = m.getParameters();
for (int i = 0; i < params.length; i++) {
printParameter(params[i]);
}
}
public static void printParameter(Parameter p) {
out.format(fmt, "Parameter class", p.getType());
out.format(fmt, "Parameter name", p.getName());
out.format(fmt, "Modifiers", p.getModifiers());
out.format(fmt, "Is implicit?", p.isImplicit());
out.format(fmt, "Is name present?", p.isNamePresent());
out.format(fmt, "Is synthetic?", p.isSynthetic());
}
public static void main(String... args) {
try {
printClassConstructors(Class.forName(args[0]));
printClassMethods(Class.forName(args[0]));
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
}
下面的輸出顯示了類 ExampleMethods
的方法和構造函數的形參名稱.(注意:記得使用-parameters選項編譯ExmapleMethods)
Number of constructors: 1
Constructor #1
public ExampleMethods()
Number of declared constructors: 1
Declared constructor #1
public ExampleMethods()
Number of methods: 4
Method #1
public boolean ExampleMethods.simpleMethod(java.lang.String,int)
Return type: boolean
Generic return type: boolean
Parameter class: class java.lang.String
Parameter name: stringParam
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Parameter class: int
Parameter name: intParam
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Method #2
public int ExampleMethods.varArgsMethod(java.lang.String...)
Return type: int
Generic return type: int
Parameter class: class [Ljava.lang.String;
Parameter name: manyStrings
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Method #3
public boolean ExampleMethods.methodWithList(java.util.List<java.lang.String>)
Return type: boolean
Generic return type: boolean
Parameter class: interface java.util.List
Parameter name: listParam
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Method #4
public <T> void ExampleMethods.genericMethod(T[],java.util.Collection<T>)
Return type: void
Generic return type: void
Parameter class: class [Ljava.lang.Object;
Parameter name: a
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Parameter class: interface java.util.Collection
Parameter name: c
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
1.getType: 返回該參數被聲明時的類型所對應的Class對象.
2.getName: 返回該參數的名稱.如果這個參數的名稱在.class文件中可以找到,那麼就返回該名稱.如果沒有找到,
那麼會自動生成一個名稱,argN.其 中N是參數的索引.
作爲一個示例,不適用-parameters選項編譯ExampleMethods源碼.那麼MethodParameterSpy將會打印出如下信息:
public boolean ExampleMethods.simpleMethod(java.lang.String,int)
Return type: boolean
Generic return type: boolean
Parameter class: class java.lang.String
Parameter name: arg0
Modifiers: 0
Is implicit?: false
Is name present?: false
Is synthetic?: false
Parameter class: int
Parameter name: arg1
Modifiers: 0
Is implicit?: false
Is name present?: false
Is synthetic?: false
3.getModifiers:返回一個Int值表示該形參的特徵.這個值是下面這些值中被應用到該形參的值的和.
4.isImplicit: 返回true如果這個參數是被隱式的聲明.
5.isNamePresent: 返回true如果該參數在.class文件中有對應的名稱.
6.isSynthetic: 返回true如果這個參數既不是顯示也不是隱式聲明的.
(詳細介紹Implicit和synthetic 請看下一小節)
隱式參數和合成參數:
有一些結構雖然沒有被顯示的在源碼中聲明,但是它卻被隱式的聲明瞭.例如 ExampleMethods類並沒有包含構造函數.一個默認的構造函數被隱式的聲明瞭.
MethodParameterSpy顯示了ExampleMethods類中隱式聲明的構造函數.
Number of declared constructors: 1
public ExampleMethods()
考慮一下情況:
public class MethodParameterExamples {
public class InnerClass { }
}
MethodParameterExamples是一個非靜態的內部類. 一個默認的構造函數被隱式的爲其聲明.然而,然而這個構造函數還包含了一個參數.當java編譯器編譯內部類的時候,它會創建一個.class文件.類似於這個:
public class MethodParameterExamples {
public class InnerClass {
final MethodParameterExamples parent;
InnerClass(final MethodParameterExamples this$0) {
parent = this$0;
}
}
}
內部類的構造函數使用包含該內部類的類作爲參數.因此,實例MethodParameterExample顯示如下:
public MethodParameterExamples$InnerClass(MethodParameterExamples)
Parameter class: class MethodParameterExamples
Parameter name: this$0
Modifiers: 32784
Is implicit?: true
Is name present?: true
Is synthetic?: false
因爲InnerClass的構造函數是隱式聲明的,那麼他的參數也是隱式聲明的.
注意:
- java編譯器爲一個內部類創建一個構造函數是爲了可以在創建包含該內部類的類時,方便的將該類的實例傳遞給該內部類.
- 值32784說明了InerClass的構造函數同時是final(16)類型和implicit(32768)
Constructs emitted by a Java compiler are marked as synthetic if they do not correspond to a construct declared explicitly or implicitly in source code, unless they are class initialization methods. Synthetic
constructs are artifacts generated by compilers that vary among different implementations. Consider the following excerpt from
Note:
- The Java compiler creates a formal parameter for the constructor of an inner class to enable the compiler to pass a reference (representing the immediately enclosing instance) from the creation expression to the member class's constructor.
- The value 32784 means that the parameter of the
InnerClass
constructor is both final (16) and implicit (32768). - The Java programming language allows variable names with dollar signs (
$
); however, by convention, dollar signs are not used in variable names.
Constructs emitted by a Java compiler are marked as synthetic if they do not correspond to a construct declared explicitly or implicitly in source code, unless they are class initialization methods. Synthetic
constructs are artifacts generated by compilers that vary among different implementations. Consider the following excerpt from MethodParameterExamples
:
考慮以下情況 MethodParameterExamples
:
public class MethodParameterExamples {
enum Colors {
RED, WHITE;
}
}
當java編譯器遇到一個枚舉結構,它會創建一系列方法.提供枚舉類型所需要的功能.例如. java編譯器會爲enum結構Color創建以個.class文件,該文件內容如下:
final class Colors extends java.lang.Enum<Colors> {
public final static Colors RED = new Colors("RED", 0);
public final static Colors BLUE = new Colors("WHITE", 1);
private final static values = new Colors[]{ RED, BLUE };
private Colors(String name, int ordinal) {
super(name, ordinal);
}
public static Colors[] values(){
return values;
}
public static Colors valueOf(String name){
return (Colors)java.lang.Enum.valueOf(Colors.class, name);
}
}
java編譯器爲Color枚舉結構創建了三個方法: Colors(String name, int ordinal)
, Colors[]
values()
, 和 Colors valueOf(String name)
.
方法values和valueOf是隱式聲明的,因此它的形參也是隱式的.
Colors(String name, int ordinal)
是一個默認的構造函數,它是隱式聲明的.然而,它的形參卻不是隱式的.因爲它的參數既不是顯示也不是隱式聲明的,所以它們是synthetic.(合成的). (枚舉類型的默認構造函數的形參不是隱式聲明,因爲不同的編譯器不一定會採用相同的形式.另外一個java編譯器可能指定不同的形參.當編譯器編譯使用枚舉常量的時候,依賴的是枚舉結構的共有的域,這些域是隱式聲明的.而不會依賴構造函數或者這些變量是怎麼被初始化的)
MethodParameterExample展示了枚舉類型Colors的相關信息:
enum Colors:
Number of constructors: 0
Number of declared constructors: 1
Declared constructor #1
private MethodParameterExamples$Colors()
Parameter class: class java.lang.String
Parameter name: $enum$name
Modifiers: 4096
Is implicit?: false
Is name present?: true
Is synthetic?: true
Parameter class: int
Parameter name: $enum$ordinal
Modifiers: 4096
Is implicit?: false
Is name present?: true
Is synthetic?: true
Number of methods: 2
Method #1
public static MethodParameterExamples$Colors[]
MethodParameterExamples$Colors.values()
Return type: class [LMethodParameterExamples$Colors;
Generic return type: class [LMethodParameterExamples$Colors;
Method #2
public static MethodParameterExamples$Colors
MethodParameterExamples$Colors.valueOf(java.lang.String)
Return type: class MethodParameterExamples$Colors
Generic return type: class MethodParameterExamples$Colors
Parameter class: class java.lang.String
Parameter name: name
Modifiers: 32768
Is implicit?: true
Is name present?: true
Is synthetic?: false
更多詳細信息,請參考 Java Language Specification
3.獲得和解析方法修飾符
方法的修飾符有以下幾種:
- Access modifiers:
public
,protected
, andprivate
- Modifier restricting to one instance:
static
- Modifier prohibiting value modification:
final
- Modifier requiring override:
abstract
- Modifier preventing reentrancy:
synchronized
- Modifier indicating implementation in another programming language:
native
- Modifier forcing strict floating point behavior:
strictfp
- Annotations
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import static java.lang.System.out;
public class MethodModifierSpy {
private static int count;
private static synchronized void inc() { count++; }
private static synchronized int cnt() { return count; }
public static void main(String... args) {
try {
Class<?> c = Class.forName(args[0]);
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods) {
if (!m.getName().equals(args[1])) {
continue;
}
out.format("%s%n", m.toGenericString());
out.format(" Modifiers: %s%n",
Modifier.toString(m.getModifiers()));
out.format(" [ synthetic=%-5b var_args=%-5b bridge=%-5b ]%n",
m.isSynthetic(), m.isVarArgs(), m.isBridge());
inc();
}
out.format("%d matching overload%s found%n", cnt(),
(cnt() == 1 ? "" : "s"));
// production code should handle this exception more gracefully
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
}
MethodModifierSpy的一些實例輸出:
$ java MethodModifierSpy java.lang.Object wait
public final void java.lang.Object.wait() throws java.lang.InterruptedException
Modifiers: public final
[ synthetic=false var_args=false bridge=false ]
public final void java.lang.Object.wait(long,int)
throws java.lang.InterruptedException
Modifiers: public final
[ synthetic=false var_args=false bridge=false ]
public final native void java.lang.Object.wait(long)
throws java.lang.InterruptedException
Modifiers: public final native
[ synthetic=false var_args=false bridge=false ]
3 matching overloads found
$ java MethodModifierSpy java.lang.StrictMath toRadians
public static double java.lang.StrictMath.toRadians(double)
Modifiers: public static strictfp
[ synthetic=false var_args=false bridge=false ]
1 matching overload found
$ java MethodModifierSpy MethodModifierSpy inc
private synchronized void MethodModifierSpy.inc()
Modifiers: private synchronized
[ synthetic=false var_args=false bridge=false ]
1 matching overload found
$ java MethodModifierSpy java.lang.Class getConstructor
public java.lang.reflect.Constructor<T> java.lang.Class.getConstructor
(java.lang.Class<T>[]) throws java.lang.NoSuchMethodException,
java.lang.SecurityException
Modifiers: public transient
[ synthetic=false var_args=true bridge=false ]
1 matching overload found
$ java MethodModifierSpy java.lang.String compareTo
public int java.lang.String.compareTo(java.lang.String)
Modifiers: public
[ synthetic=false var_args=false bridge=false ]
public int java.lang.String.compareTo(java.lang.Object)
Modifiers: public volatile
[ synthetic=true var_args=false bridge=true ]
2 matching overloads found
注意到 Method.isVarArgs()
返回 true
對於 Class.getConstructor()
.
這些說明了該方法的聲明應該看起來如下:
public Constructor<T> getConstructor(Class<?>... parameterTypes)
而不是這樣
public Constructor<T> getConstructor(Class<?> [] parameterTypes)
Notice that the output for
String.compareTo()
contains
two methods. The method declared in String.java
:注意到對於方法 String.compareTo()
包含了兩個方法,該方法被聲明在String.java中:
public int compareTo(String anotherString);