深入理解 Java 反射:Method (成员方法)

r囧r小猫 2022-07-11 07:12 341阅读 0赞

深入理解 Java 反射系列:

  • 深入理解 Java 反射:Class (反射的入口)
  • 深入理解 Java 反射:Field (成员变量)
  • 深入理解 Java 反射:Method (成员方法)

读完本文你将了解到:

    • Method 介绍
    • 获取方法的信息
    • 获取方法的参数名称
    • 获取方法的修饰符

      • synthetic method合成方法
      • varargs variable arguments methodJava 可变参数方法
      • bridge method桥接方法
    • 反射调用方法
    • 调用含有可变参数的方法
    • 常见错误 1 泛型擦除导致的 NoSuchMethodException
    • 常见错误 2 访问不可见方法导致的 IllegalAccessException
    • 常见错误 3反射调用方法时传入错误参数导致的 IllegalArgumentException
    • Thanks

Method 介绍

继承的方法(包括重载、重写和隐藏的)会被编译器强制执行,这些方法都无法反射。

因此,反射一个类的方法时不考虑父类的方法,只考虑当前类的方法。

每个方法都由 修饰符、返回值、参数、注解和抛出的异常组成。

java.lang.reflect.Method 方法为我们提供了获取上述部分的 API。

获取方法的信息

下面的代码演示了如何获得一个方法的 修饰符、返回值、参数、注解和抛出的异常 等信息:

  1. public class MethodTypeSpy extends BaseTestClass {
  2. private static final String fmt = "%24s: %s\n";
  3. private static final String HELLO_WORLD = "I'm cute shixin";
  4. @Deprecated
  5. public static void main(String[] args) throws ClassNotFoundException {
  6. MethodTypeSpy methodTypeSpy = new MethodTypeSpy();
  7. Class<? extends MethodTypeSpy> cls = methodTypeSpy.getClass();
  8. printFormat("Class:%s \n", cls.getCanonicalName());
  9. Method[] declaredMethods = cls.getDeclaredMethods();
  10. for (Method declaredMethod : declaredMethods) {
  11. printFormat(fmt, "Method name", declaredMethod.getName()); //获得单独的方法名
  12. //获得完整的方法信息(包括修饰符、返回值、路径、名称、参数、抛出值)
  13. printFormat(fmt, "toGenericString", declaredMethod.toGenericString());
  14. int modifiers = declaredMethod.getModifiers(); //获得修饰符
  15. printFormat(fmt, "Modifiers", Modifier.toString(modifiers));
  16. System.out.format(fmt, "ReturnType", declaredMethod.getReturnType()); //获得返回值
  17. System.out.format(fmt, "getGenericReturnType", declaredMethod.getGenericReturnType());//获得完整信息的返回值
  18. Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); //获得参数类型
  19. Type[] genericParameterTypes = declaredMethod.getGenericParameterTypes();
  20. for (int i = 0; i < parameterTypes.length; i++) {
  21. System.out.format(fmt, "ParameterType", parameterTypes[i]);
  22. System.out.format(fmt, "GenericParameterType", genericParameterTypes[i]);
  23. }
  24. Class<?>[] exceptionTypes = declaredMethod.getExceptionTypes(); //获得异常名称
  25. Type[] genericExceptionTypes = declaredMethod.getGenericExceptionTypes();
  26. for (int i = 0; i < exceptionTypes.length; i++) {
  27. System.out.format(fmt, "ExceptionTypes", exceptionTypes[i]);
  28. System.out.format(fmt, "GenericExceptionTypes", genericExceptionTypes[i]);
  29. }
  30. Annotation[] annotations = declaredMethod.getAnnotations(); //获得注解
  31. for (Annotation annotation : annotations) {
  32. System.out.format(fmt, "Annotation", annotation);
  33. System.out.format(fmt, "AnnotationType", annotation.annotationType());
  34. }
  35. }
  36. }
  37. }

查看当前类 MethodTypeSpy的方法 main() 的信息,运行结果:

  1. Classnet.sxkeji.shixinandroiddemo2.test.reflection.MethodTypeSpy
  2. Method name: main
  3. toGenericString: public static void net.sxkeji.shixinandroiddemo2.test.reflection.MethodTypeSpy.main(java.lang.String[]) throws java.lang.ClassNotFoundException
  4. Modifiers: public static
  5. ReturnType: void
  6. getGenericReturnType: void
  7. ParameterType: class [Ljava.lang.String;
  8. GenericParameterType: class [Ljava.lang.String;
  9. ExceptionTypes: class java.lang.ClassNotFoundException
  10. GenericExceptionTypes: class java.lang.ClassNotFoundException
  11. Annotation: @java.lang.Deprecated()
  12. AnnotationType: interface java.lang.Deprecated
  13. Process finished with exit code 0

获取方法的参数名称

从 JDK 1.8 开始,java.lang.reflect.Executable.getParameters 为我们提供了获取普通方法或者构造方法的名称的能力。

在 JDK 中 java.lang.reflect.Methodjava.lang.reflect.Constructor 都继承自 Executable,因此它俩也有同样的能力。

然而在 Android SDK 中 Method, Constructor 继承自 AbstractMethod,无法获得方法的参数名:

  1. public final class Method extends AbstractMethod implements GenericDeclaration, Member

你可以在 J2EE 环境下练习官方的 获取参数名称代码

  1. /*
  2. * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * - Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. *
  11. * - Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. *
  15. * - Neither the name of Oracle or the names of its
  16. * contributors may be used to endorse or promote products derived
  17. * from this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  20. * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  21. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  22. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  23. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  24. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  25. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  26. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  27. * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  28. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. */
  31. import java.lang.reflect.*;
  32. import java.util.function.*;
  33. import static java.lang.System.out;
  34. public class MethodParameterSpy {
  35. private static final String fmt = "%24s: %s%n";
  36. // for the morbidly curious
  37. <E extends RuntimeException> void genericThrow() throws E {}
  38. public static void printClassConstructors(Class c) {
  39. Constructor[] allConstructors = c.getConstructors();
  40. out.format(fmt, "Number of constructors", allConstructors.length);
  41. for (Constructor currentConstructor : allConstructors) {
  42. printConstructor(currentConstructor);
  43. }
  44. Constructor[] allDeclConst = c.getDeclaredConstructors();
  45. out.format(fmt, "Number of declared constructors",
  46. allDeclConst.length);
  47. for (Constructor currentDeclConst : allDeclConst) {
  48. printConstructor(currentDeclConst);
  49. }
  50. }
  51. public static void printClassMethods(Class c) {
  52. Method[] allMethods = c.getDeclaredMethods();
  53. out.format(fmt, "Number of methods", allMethods.length);
  54. for (Method m : allMethods) {
  55. printMethod(m);
  56. }
  57. }
  58. public static void printConstructor(Constructor c) {
  59. out.format("%s%n", c.toGenericString());
  60. Parameter[] params = c.getParameters();
  61. out.format(fmt, "Number of parameters", params.length);
  62. for (int i = 0; i < params.length; i++) {
  63. printParameter(params[i]);
  64. }
  65. }
  66. public static void printMethod(Method m) {
  67. out.format("%s%n", m.toGenericString());
  68. out.format(fmt, "Return type", m.getReturnType());
  69. out.format(fmt, "Generic return type", m.getGenericReturnType());
  70. Parameter[] params = m.getParameters();
  71. for (int i = 0; i < params.length; i++) {
  72. printParameter(params[i]);
  73. }
  74. }
  75. public static void printParameter(Parameter p) {
  76. out.format(fmt, "Parameter class", p.getType());
  77. out.format(fmt, "Parameter name", p.getName());
  78. out.format(fmt, "Modifiers", p.getModifiers());
  79. out.format(fmt, "Is implicit?", p.isImplicit());
  80. out.format(fmt, "Is name present?", p.isNamePresent());
  81. out.format(fmt, "Is synthetic?", p.isSynthetic());
  82. }
  83. public static void main(String... args) {
  84. try {
  85. printClassConstructors(Class.forName(args[0]));
  86. printClassMethods(Class.forName(args[0]));
  87. } catch (ClassNotFoundException x) {
  88. x.printStackTrace();
  89. }
  90. }
  91. }

获取方法的修饰符

方法可以被以下修饰符修饰:

  • 访问权限控制符:public, protected, private
  • 限制只能有一个实例的:static
  • 不允许修改的:final
  • 抽象,要求子类重写:abstract
  • 预防重入的同步锁:synchronized
  • 用其他语言实现的方法:native
  • 严格的浮点型强度:strictfp
  • 注解

类似获取 Class 的修饰符,我们可以使用 “Method.getModifiers()方法获取当前成员变量的修饰符。 返回java.lang.reflect.Modifier“` 中定义的整形值。

举个例子:

  1. public class MethodModifierSpy extends BaseTestClass {
  2. private final static String CLASS_NAME = "java.lang.String";
  3. public static void main(String[] args) {
  4. MethodModifierSpy methodModifierSpy = new MethodModifierSpy();
  5. Class<? extends MethodModifierSpy> cls = methodModifierSpy.getClass();
  6. printFormat("Class: %s \n\n", cls.getCanonicalName());
  7. Method[] declaredMethods = cls.getDeclaredMethods();
  8. for (Method declaredMethod : declaredMethods) {
  9. printFormat("\n\nMethod name: %s \n", declaredMethod.getName());
  10. printFormat("Method toGenericString: %s \n", declaredMethod.toGenericString());
  11. int modifiers = declaredMethod.getModifiers();
  12. printFormat("Method Modifiers: %s\n", Modifier.toString(modifiers));
  13. System.out.format("synthetic= %-5b, var_args= %-5b, bridge= %-5b \n"
  14. , declaredMethod.isSynthetic(), declaredMethod.isVarArgs(), declaredMethod.isBridge());
  15. }
  16. }
  17. public final void varArgsMethod(String... strings) {
  18. }
  19. }

运行结果:

  1. Class: net.sxkeji.shixinandroiddemo2.test.reflection.MethodModifierSpy
  2. Method name main
  3. Method toGenericString public static void net.sxkeji.shixinandroiddemo2.test.reflection.MethodModifierSpy.main(java.lang.String[])
  4. Method Modifiers public static
  5. synthetic= false, var_args= false, bridge= false
  6. Method name varArgsMethod
  7. Method toGenericString public final void net.sxkeji.shixinandroiddemo2.test.reflection.MethodModifierSpy.varArgsMethod(java.lang.String...)
  8. Method Modifiers public final transient
  9. synthetic= false, var_args= true , bridge= false
  10. Process finished with exit code 0

注意:上面的最后一行可以看到,方法有三种类型:synthetic, varagrs, bridge。

下面介绍这三种方法类型:

synthetic method:合成方法

这个知识点主要学习自:http://www.oschina.net/code/snippet_2438265_54869

什么是合成方法呢?

首先需要理解一个概念:

对于 Java 编译器而言,内部类也会被单独编译成一个class文件。
那么原有代码中的相关属性可见性就难以维持,synthetic method也就是为了这个目的而生成的。生成的synthetic方法是包访问性的static方法.

还是有些抽象,举个例子:

  1. public class Foo {
  2. private Object baz = "Hello";
  3. private int get(){
  4. return 1;
  5. }
  6. private class Bar {
  7. private Bar() {
  8. System.out.println(get());
  9. }
  10. }
  11. }

上面的代码中,Bar 访问了 Foo 的 private 方法 get()。

使用 javap -private Foo看一下:

  1. public class Foo {
  2. private java.lang.Object baz;
  3. public Foo();
  4. private int get();
  5. static int access$000(Foo); //多出来的 synthetic 方法,为了在 Bar 中的这段代码 System.out.println(get());
  6. }

因此可以这么理解:

Synthetic (合成)方法是由编译器产生的、源代码中没有的方法。
当内部类与外部类之前有互相访问 private 属性、方法时,编译器会在运行时为调用方创建一个 synthetic 方法。

合成方法主要创建于嵌套内部类中。

我们可以使用 Method.isSynthetic() 方法判断某个方法是否为 synthetic 。

varargs ( variable arguments) method:Java 可变参数方法

  1. public void testVarargs(String... strings){
  2. //...
  3. }
  • 创建时必须放在方法尾部,即一个方法只能有一个可变数组参数
  • 调用时可以传入一个数组:
  1. * `testVarargs(new String[]{"shixin","zhang"});`
  • 也可以分别传入多个参数:
  1. * `testVarargs("shixin","zhang");`

推荐使用后者。

我们可以使用 Method.isVarArgs() 方法判断某个方法包含可变参数 。

bridge method:桥接方法

这个知识点主要学习自:http://blog.csdn.net/mhmyqn/article/details/47342577

桥接方法是为了泛型的向前兼容提出的,不太熟悉泛型的同学可以查看《
Java 进阶巩固:深入理解 泛型》。

我们知道,为了兼容 JDK 1.5 以前的代码,泛型会在编译时被去除(泛型擦除),这时需要创建桥接方法

举个例子:

  1. /**
  2. * @author Mikan
  3. * @date 2015-08-05 16:22
  4. */
  5. public interface SuperClass<T> {
  6. T method(T param);
  7. }
  8. package com.mikan;
  9. /**
  10. * @author Mikan
  11. * @date 2015-08-05 17:05
  12. */
  13. public class SubClass implements SuperClass<String> {
  14. public String method(String param) {
  15. return param;
  16. }
  17. }

上面的代码创建了一个泛型接口和实现类。

实现类在运行时的字节码如下:

  1. localhost:mikan mikan$ javap -c SubClass.class
  2. Compiled from "SubClass.java"
  3. public class com.mikan.SubClass implements com.mikan.SuperClass<java.lang.String> {
  4. public com.mikan.SubClass();
  5. flags: ACC_PUBLIC
  6. Code:
  7. stack=1, locals=1, args_size=1
  8. 0: aload_0
  9. 1: invokespecial #1 // Method java/lang/Object."<init>":()V
  10. 4: return
  11. LineNumberTable:
  12. line 7: 0
  13. LocalVariableTable:
  14. Start Length Slot Name Signature
  15. 0 5 0 this Lcom/mikan/SubClass;
  16. public java.lang.String method(java.lang.String);
  17. flags: ACC_PUBLIC
  18. Code:
  19. stack=1, locals=2, args_size=2
  20. 0: aload_1
  21. 1: areturn
  22. LineNumberTable:
  23. line 11: 0
  24. LocalVariableTable:
  25. Start Length Slot Name Signature
  26. 0 2 0 this Lcom/mikan/SubClass;
  27. 0 2 1 param Ljava/lang/String;
  28. public java.lang.Object method(java.lang.Object);
  29. flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
  30. Code:
  31. stack=2, locals=2, args_size=2
  32. 0: aload_0
  33. 1: aload_1
  34. 2: checkcast #2 // class java/lang/String
  35. 5: invokevirtual #3 // Method method:(Ljava/lang/String;)Ljava/lang/String;
  36. 8: areturn
  37. LineNumberTable:
  38. line 7: 0
  39. LocalVariableTable:
  40. Start Length Slot Name Signature
  41. 0 9 0 this Lcom/mikan/SubClass;
  42. 0 9 1 x0 Ljava/lang/Object;
  43. }

可以看到,实现类的字节码中多了两个方法,一个是默认的无参构造方法,另一个就是编译器自动生成的桥接方法(flags 包括 ACC_BRIDGEACC_SYNTHETIC),它的参数、返回值类型都是 Object。但是它把 Object 类型的参数强制转换成了 String 类型,再调用在 SubClass 类中声明的方法,转换过来其实就是:

  1. public Object method(Object param) {
  2. return this.method(((String) param));
  3. }

可以看到,桥接方法的参数、返回值和 JDK 1.5 以前的“泛型”方法一样,都是 Object,实际上调用的却是真正的泛型方法。

明修栈道暗度陈仓啊。有些类似适配器模式。

小结一下:

桥接方法由编译器自动生成,参数、返回值都是 Object,然后调用实际泛型方法。

它实现了将泛型生成的字节码与 1.5 以前的字节码进行兼容。

我们可以使用 Method.isBridge() 方法判断某个方法是否为桥接方法 。

反射调用方法

我们可以使用 java.lang.reflect.Method.invoke() 方法来反射调用一个方法(下面的代码是 JDK 1.6):

  1. public native Object invoke(Object receiver, Object... args)
  2. throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
  • 第一个参数是方法属于的对象(如果是静态方法,则可以直接传 null)
  • 第二个可变参数是该方法的参数
  • 如果调用的方法有抛出异常,异常会被 java.lang.reflect.InvocationTargetException 包一层

当然一般只用于正常情况下无法直接访问的方法(比如:private 的方法,或者无法或者该类的对象)。

举个例子:

  1. public class MethodInvoke extends BaseTestClass {
  2. private boolean checkString(String s) {
  3. printFormat("checkString: %s\n", s);
  4. return TextUtils.isEmpty(s);
  5. }
  6. private static void saySomething(String something) {
  7. System.out.println(something);
  8. }
  9. private String onEvent(TestEvent event) {
  10. System.out.format("Event name: %s\n", event.getEventName());
  11. return event.getResult();
  12. }
  13. static class TestEvent {
  14. private String eventName;
  15. private String result;
  16. public TestEvent(String eventName, String result) {
  17. this.eventName = eventName;
  18. this.result = result;
  19. }
  20. public String getResult() {
  21. return result;
  22. }
  23. public String getEventName() {
  24. return eventName;
  25. }
  26. }
  27. public static void main(String[] args) {
  28. try {
  29. Class<?> cls = Class.forName("net.sxkeji.shixinandroiddemo2.test.reflection.MethodInvoke");
  30. MethodInvoke object = (MethodInvoke) cls.newInstance();
  31. Method[] declaredMethods = cls.getDeclaredMethods();
  32. for (Method declaredMethod : declaredMethods) {
  33. String methodName = declaredMethod.getName(); //获取方法名
  34. Type returnType = declaredMethod.getGenericReturnType(); //获取带泛型的返回值类型
  35. int modifiers = declaredMethod.getModifiers(); //获取方法修饰符
  36. // declaredMethod.setAccessible(true);
  37. if (methodName.equals("onEvent")) {
  38. TestEvent testEvent = new TestEvent("shixin's Event", "cuteType");
  39. try {
  40. Object invokeResult = declaredMethod.invoke(object, testEvent);
  41. System.out.format("Invoke of %s, return %s \n", methodName, invokeResult.toString());
  42. } catch (InvocationTargetException e) { //处理被调用方法可能抛出的异常
  43. Throwable cause = e.getCause();
  44. System.out.format("Invocation of %s failed: %s\n", methodName, cause.getMessage());
  45. }
  46. } else if (returnType == boolean.class) {
  47. try {
  48. declaredMethod.invoke(object, "shixin's parameter");
  49. } catch (InvocationTargetException e) {
  50. Throwable cause = e.getCause();
  51. System.out.format("Invocation of %s failed: %s\n", methodName, cause.getMessage());
  52. }
  53. }else if (Modifier.isStatic(modifiers) && !methodName.equals("main")){ //静态方法,调用时 object 直接传入 null
  54. try {
  55. declaredMethod.invoke(null, "static method");
  56. } catch (InvocationTargetException e) {
  57. Throwable cause = e.getCause();
  58. System.out.format("Invocation of %s failed: %s\n", methodName, cause.getMessage());
  59. }
  60. }
  61. }
  62. } catch (ClassNotFoundException e) {
  63. e.printStackTrace();
  64. } catch (InstantiationException e) {
  65. e.printStackTrace();
  66. } catch (IllegalAccessException e) {
  67. e.printStackTrace();
  68. }
  69. }
  70. }

运行结果:

  1. checkString: shixin's parameter
  2. Invocation of checkString failed: Stub!
  3. Event name: shixin's Event
  4. Invoke of onEvent, return cuteType
  5. static method
  6. Process finished with exit code 0

调用含有可变参数的方法

首先需要理解的是,可变参数是用一个数组实现的。

Class.getDeclaredMethod(name, parameterTypes) 方法为我们提供了获取有可变参数的方法:

  1. public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
  2. throws NoSuchMethodException {
  3. return getMethod(name, parameterTypes, false);
  4. }

可以看到,第二个参数是 Class 类型的可变参数,我们在调用时可以传入一个 Class 数组。

下面的代码演示了如何调用一个含有可变参数方法:

  1. public class VarArgsMethodInvoke extends BaseTestClass {
  2. public void printVarArgs(String... varArgs) {
  3. System.out.format("printVarArgs:\n");
  4. for (String arg : varArgs) {
  5. System.out.format("%20s\n", arg);
  6. }
  7. }
  8. public static void main(String[] args) {
  9. VarArgsMethodInvoke object = new VarArgsMethodInvoke();
  10. Class<? extends VarArgsMethodInvoke> cls = object.getClass();
  11. try {
  12. // Class[] argTypes = new Class[]{String[].class};
  13. Method declaredMethod = cls.getDeclaredMethod("printVarArgs", String[].class);
  14. String[] varArgs = {
  15. "shixin", "zhang"};
  16. declaredMethod.invoke(object, (Object) varArgs);
  17. } catch (InvocationTargetException e) {
  18. e.printStackTrace();
  19. } catch (IllegalAccessException e) {
  20. e.printStackTrace();
  21. } catch (NoSuchMethodException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. }

运行结果:

  1. printVarArgs
  2. shixin
  3. zhang
  4. Process finished with exit code 0

常见错误 1 :泛型擦除导致的 NoSuchMethodException

  1. public class MethodReflectionFailed<T> extends BaseTestClass {
  2. public void lookUp(T t){}
  3. public void find(Integer integer){}
  4. public static void main(String[] args) {
  5. //虽然声明类型为 Integer,实际会被擦除
  6. Class<? extends MethodReflectionFailed> cls = (new MethodReflectionFailed<Integer>()).getClass();
  7. // Class<Integer> parameterClass = Integer.class;
  8. Class<Object> parameterClass = Object.class;
  9. try {
  10. Method lookUp = cls.getMethod("lookUp", parameterClass);
  11. printFormat("Method: %s\n", lookUp.toGenericString());
  12. } catch (NoSuchMethodException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. }```
  17. 反射调用泛型方法时,由于运行前编译器已经把泛型擦除,**参数类型会被擦除为上边界(默认 Object)**。
  18. 这时你想调用的 ```lookup(Integer)``` 是不存在的,因为它实际上是 ```lookup(Object)```,上述代码运行结果:
  19. ```java
  20. java.lang.NoSuchMethodException: net.sxkeji.shixinandroiddemo2.test.reflection.MethodReflectionFailed.lookUp(java.lang.Integer)
  21. at java.lang.Class.getMethod(Class.java:1786)
  22. at net.sxkeji.shixinandroiddemo2.test.reflection.MethodReflectionFailed.main(MethodReflectionFailed.java:25)
  23. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  24. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  25. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  26. at java.lang.reflect.Method.invoke(Method.java:498)
  27. at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
  28. <div class="se-preview-section-delimiter"></div>

只要传入的参数改为 Object 就可以了:

  1. Method: public void net.sxkeji.shixinandroiddemo2.test.reflection.MethodReflectionFailed.lookUp(T)

小结:反射调用方法时要传入上边界。

常见错误 2 :访问不可见方法导致的 IllegalAccessException

当你访问 private 的方法或者 private 的类中的方法,会抛出这个异常。

解决方法就是给该 method 设置 setAccessible(true)

注意:我们无法访问 private 的方法是因为有权限管理机制,setAccessible(true) 只是发出允许访问当前方法的请求,并不能保证一定成功。在成功后我们才可以反射调用。

常见错误 3:反射调用方法时传入错误参数导致的 IllegalArgumentException

如果一个方法没有参数,但是我们反射时传入参数,就会导致 llegalArgumentException

此外,当声明一个可变参数方法 foo(Object... o) 时,编译器会使用一个 Object 数组将所有参数传过去。
也就是说 foo(Object... o) 相当于 foo(Object[] o)

Thanks

http://docs.oracle.com/javase/tutorial/reflect/member/method.html
http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.4.5
http://blog.csdn.net/mhmyqn/article/details/47342577
http://www.oschina.net/code/snippet_2438265_54869

发表评论

表情:
评论列表 (有 0 条评论,341人围观)

还没有评论,来说两句吧...

相关阅读