Java反射基礎(三)--Methods對象的使用

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 ..


因此T被取代使用向上轉型原則.在這個示例中便是java.lang.Object了.

最後一個示例程序展示了一個輸出一個多次重載的方法的例子:

$ 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


MethodParameterSpy 實例程序使用了Parameter類中的這些方法:

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:

由java編譯器生成, 並且該結構在源代碼中沒有被顯示或者隱式的聲明,並且該結構不是類初始化方法.則該結果被標記爲synthetic.不同的編譯器會生成不同的合成結構.

考慮以下情況 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: publicprotected, and private
  • 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


實例程序MethodModifierSpy 會列出給定方法名的修飾符.同時它還展示了該方法是不是合成的,是不是可變參數的等.

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);



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章