美文网首页
(5) Method类的基本使用

(5) Method类的基本使用

作者: Mrsunup | 来源:发表于2018-10-06 00:59 被阅读0次

    上面一小节讲解了Field字段的用法,包括如何获取一个字段的信息,字段的修饰符,字段的类型,以及如何访问一个字段和给字段进行设值。

    这一小节将讲解method类的使用,将从下面几个方法来介绍使用:

    • 获取方法的类型信息 (返回方法的名称,返回值类型,参数类型,异常类型,注解信息)
    • 获取方法参数的信息
    • 解析和检索方法修饰符
    • 方法的调用
    • 方法的常用的错误

    1.获取方法的类型信息

    接下来的MethodSpy例子阐述了在一个给定的类中获取方法的名称,返回类型,参数类型以及异常类型

    /**
     * 扫描类中方法的信息 (获取方法的名称,返回值,参数,异常信息)
     */
    public class MethodSpy {
        private static final String fmt = "%24s: %s%n";
    
        public static void main(String... args) {
            args = new String[]{Class.class.getName(), "getConstructor"};
            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.getName());
                    //对比方法toGenericString 和toString 的信息,发现toGenericString有泛型信息输出
                    out.format("%s%n", m.toGenericString());
                    out.format("%s%n", m.toString());
    
                    //得到方法的返回类型的信息
                    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();
            }
        }
    }
    

    输出结果如下:

    getConstructor
    public java.lang.reflect.Constructor<T> java.lang.Class.getConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException,java.lang.SecurityException
    public java.lang.reflect.Constructor 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
    

    2.获取方法参数的信息

    下面的例子说明了如何获取参数信息:

    import java.lang.reflect.Method;
    import java.lang.reflect.Parameter;
    import java.util.Collection;
    import java.util.List;
    import static java.lang.System.out;
    
    /**
     * @Project: jdk
     * @description: 输出方法的参数信息
     * @author: sunkang
     * @create: 2018-10-05 21:32
     * @ModificationHistory who      when       What
     **/
    public class MethodParameterTest {
    
        public boolean simpleMethod( final String stringParam, int intParam) {
            System.out.println("String: " + stringParam + ", integer: " + intParam);
            return true;
        }
    
        public int varArgsMethod(String... manyStrings) {
            return manyStrings.length;
        }
    
        public boolean methodWithList(List<String> listParam) {
            return listParam.isEmpty();
        }
    
        public <T> void genericMethod(T[] a, Collection<T> c) {
            System.out.println("Length of array: " + a.length);
            System.out.println("Size of collection: " + c.size());
        }
    
        private static final String  fmt = "%24s: %s%n";
    
        public static void printParameter(Parameter p) {
            //p.getType(); 返回参数类型
            out.format(fmt, "Parameter class", p.getType());
            // p.getName();  返回参数名称,都是编译后的参数名称,名称的格式为argN,N为参数的序号
            out.format(fmt, "Parameter name", p.getName());
            //p.getModifiers() 得到参数的修饰符,
            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());
            //参数是否是可变参数  varArgsMethod该方法中参数是可变参数
            out.format(fmt, "Is varArgs?", p.isVarArgs());
        }
    
        public static void main(String[] args) {
            //得到MethodParameterTest的所有的方法
           Method[]  methods =  MethodParameterTest.class.getDeclaredMethods();
           for(Method method :methods){
               out.println("--------------------------");
               out.format("%s%n", method.toGenericString());
               out.format(fmt, "Return type", method.getReturnType());
               out.format(fmt, "Generic return type", method.getGenericReturnType());
               Parameter[]  parameters =   method.getParameters();
               for(Parameter parameter : parameters){
                   printParameter(parameter);
               }
           }
        }
    }
    

    输出结果为: 发现参数的名称为arg0,arg1 这种格式,参数的修饰符的modify的值一般来说为0,这里测试的跟反射的官方文档的说明有点不一致。具体的说明见https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html#implcit_and_synthetic

    --------------------------
    public static void com.java.reflect.merbers.methods.MethodParameterTest.main(java.lang.String[])
                 Return type: void
         Generic return type: void
             Parameter class: class [Ljava.lang.String;
              Parameter name: arg0
                   Modifiers: 0
                Is implicit?: false
            Is name present?: false
               Is synthetic?: false
                 Is varArgs?: false
    --------------------------
    public boolean com.java.reflect.merbers.methods.MethodParameterTest.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
                 Is varArgs?: false
             Parameter class: int
              Parameter name: arg1
                   Modifiers: 0
                Is implicit?: false
            Is name present?: false
               Is synthetic?: false
                 Is varArgs?: false
    --------------------------
    public <T> void com.java.reflect.merbers.methods.MethodParameterTest.genericMethod(T[],java.util.Collection<T>)
                 Return type: void
         Generic return type: void
             Parameter class: class [Ljava.lang.Object;
              Parameter name: arg0
                   Modifiers: 0
                Is implicit?: false
            Is name present?: false
               Is synthetic?: false
                 Is varArgs?: false
             Parameter class: interface java.util.Collection
              Parameter name: arg1
                   Modifiers: 0
                Is implicit?: false
            Is name present?: false
               Is synthetic?: false
                 Is varArgs?: false
    --------------------------
    public static void com.java.reflect.merbers.methods.MethodParameterTest.printParameter(java.lang.reflect.Parameter)
                 Return type: void
         Generic return type: void
             Parameter class: class java.lang.reflect.Parameter
              Parameter name: arg0
                   Modifiers: 0
                Is implicit?: false
            Is name present?: false
               Is synthetic?: false
                 Is varArgs?: false
    --------------------------
    public int com.java.reflect.merbers.methods.MethodParameterTest.varArgsMethod(java.lang.String...)
                 Return type: int
         Generic return type: int
             Parameter class: class [Ljava.lang.String;
              Parameter name: arg0
                   Modifiers: 0
                Is implicit?: false
            Is name present?: false
               Is synthetic?: false
                 Is varArgs?: true
    --------------------------
    public boolean com.java.reflect.merbers.methods.MethodParameterTest.methodWithList(java.util.List<java.lang.String>)
                 Return type: boolean
         Generic return type: boolean
             Parameter class: interface java.util.List
              Parameter name: arg0
                   Modifiers: 0
                Is implicit?: false
            Is name present?: false
               Is synthetic?: false
                 Is varArgs?: false
    

    3.解析和检索方法修饰符

    下面的这个例子,展示了如何获取方法的修饰符,以及判断方法是否是合成的,判断方法是否是一种桥接的方法,判断方法是否包含可变参数

    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) {
    
    //      args= new  String[]{Object.class.getName(),"wait"};
    //      args= new  String[]{MethodModifierSpy.class.getName(),"main"};
            args= new  String[]{String.class.getName(),"compareTo"};
        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()));
            //m.isVarArgs() 返回方法是否是包含可变参数
            //m.isBridge()  如果为true ,说明是一种桥接的方法,避免泛型擦除后,泛型信息不再,避免发生编译错误,所以在增加了一个桥接的方法
                /**
                 * 原有的接口信息为: public interface Comparable<T> 中的方法public int compareTo(T o);,泛型为T,泛型擦除之后,
                 * 就会变成public int compareTo(java.lang.Ojbect o)
                 *
                 * 比如String 实现了Comparable<String>该接口,必须要实现泛型擦除后的public int compareTo(java.lang.Ojbect o)该接口,编译才不会报错
                 * public int java.lang.String.compareTo(java.lang.Object)
                 *   Modifiers:  public volatile
                 *   [ synthetic=true  var_args=false bridge=true  ]
                 */
            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();
        }
        }
    }
    

    输出结果为: 可以发现多了个public int java.lang.String.compareTo(java.lang.Object)的方法,该方法是一个桥接的方法,为了避免泛型擦除之后编译出错而自动增加的一个方法

    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
    

    3.方法的调用

    下面的例子,展示了普通方法,参数为数组或者可变参数的方法还有静态方法的调用案例

    /**
     * @Project: jdk
     * @description:   方法的调用测试  
     * @author: sunkang
     * @create: 2018-09-28 23:39
     * @ModificationHistory who      when       What
     **/
    public class MethodInvokeTest {
    
        private  void test(String name,int age){
            System.out.println("name:"+name+"age:"+age);
        }
    
        private static void staticTest(String name,int age){
            System.out.println("name:"+name+",age:"+age);
        }
    
        private  void test(String ... args){
            System.out.println("args:"+args);
        }
    
        public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            MethodInvokeTest test  =new MethodInvokeTest();
            //1.普通方法的调用
          Method method =   MethodInvokeTest.class.getDeclaredMethod("test",String.class,int.class);
          method.invoke(test,"sunkang",22);
    
          //2.参数为可变参数的或者数组的情况的调用
            Method vargMethod=   MethodInvokeTest.class.getDeclaredMethod("test",String[].class);
            //这里必须强转为一个Object对象才可以
            vargMethod.invoke(test, (Object)new String[]{"sun","kang","Mr"});
    
            //3.静态方法的反射调用,此时调用的对象一般传入null就行,传入test对象也是可以的,但是没有意义
            Method staticMethod=   MethodInvokeTest.class.getDeclaredMethod("staticTest",String.class,int.class);
            staticMethod.invoke(null, "sunkang",22);
        }
    }
    

    输出结果为:

    name:sunkangage:22
    args:[Ljava.lang.String;@4554617c
    name:sunkang,age:22
    

    4.方法的常用的错误

    • 1.由于泛型参数导致方法找不到
    /**
     * 由于泛型会被擦除,编译器会用其上限类型替换泛型类型,<T> 的T上限就是 Object, <T extends Integer>那么T就是上限就是Integer
     * @param <T>
     */
    public class MethodTrouble<T>  {
        //编译之后就是   public void com.java.reflect.merbers.methods.MethodTrouble.lookup(java.lang.Object)
        //   T extends Integer 那么就是上限就是Integer
        public void lookup(T t) {}
        public void find(Integer i) {}
    
        public static void main(String... args) {
            args = new  String[]{"lookup","java.lang.Integer"};
            //解决方案可以
    //      args  = new String[]{"lookup","java.lang.Object"};
            try {
                String mName = args[0];
                Class cArg = Class.forName(args[1]);
    
                Class<?> c = (new MethodTrouble<Integer>()).getClass();
                Method m = c.getMethod(mName, cArg);
                System.out.format("Found:%n  %s%n", m.toGenericString());
            } catch (NoSuchMethodException x) {
                x.printStackTrace();
            } catch (ClassNotFoundException x) {
                x.printStackTrace();
            }
        }
    }
    

    结果为: 解决方案已经在代码中写了

    java.lang.NoSuchMethodException: com.java.reflect.merbers.methods.MethodTrouble.lookup(java.lang.Integer)
        at java.lang.Class.getMethod(Class.java:1786)
        at com.java.reflect.merbers.methods.MethodTrouble.main(MethodTrouble.java:24)
    
    • 2.方法的访问权限不够导致错误
    /**
     * 方法访问权限不够导致调用错误   需要设置m.setAccessible(true);可以绕过访问的检查
     */
    public class MethodTroubleAgain {
        public static void main(String... args) {
        AnotherClass ac = new AnotherClass();
        try {
            Class<?> c = ac.getClass();
            Method m = c.getDeclaredMethod("m");
            //解决方案如下,设置 m.setAccessible(true)为true
            //m.setAccessible(true);      // solution
            Object o = m.invoke(ac);    // IllegalAccessException
    
        } catch (NoSuchMethodException x) {
            x.printStackTrace();
        } catch (InvocationTargetException x) {
            x.printStackTrace();
        } catch (IllegalAccessException x) {
            x.printStackTrace();
        }
        }
    }
    class AnotherClass {
        private void m() {}
    }
    
    • 3.Method.invoke()调用过程中参数不正确引发的异常
    /**
     * 反射的无参的调用的测试
     * 
     * 对于反射调用无参的情况 ,符合调用的方式有三种:
     * 1.   m.invoke(obj)    传入一个对象,参数不传
     * 2.   m.invoke(obj,null)    参数传null,编译会有警告,但是可以正常调用
     * 3.   m.invoke(obj,new ObJect[0])   参数传一个空的数组,对于可变参数来说相当于没有参数
     */
    public class MethodTroubleToo {
        
        public void ping() {
            System.out.format("PONG!%n");
        }
    
        /**
         * foo(Object... o) 在编译的时候会 参数类型转变为数组类型,也就是foo(Object[] o)
         * @param args
         */
        public void foo(Object ... args){
    
        }
        
        public static void main(String... args) {
            args = new String[]{ "3"};
            try {
                MethodTroubleToo mtt = new MethodTroubleToo();
                Method m = MethodTroubleToo.class.getMethod("ping");
    
                switch (Integer.parseInt(args[0])) {
                    case 0:
                        m.invoke(mtt);                 // works
                        break;
                    case 1:
                        m.invoke(mtt, null);           // works (expect compiler warning) 编译警告,但是可以成功运行
                    break;
                    case 2:
                        Object arg2 = null;           //arg2 的类型为Object,实际上是没有参数类型进行匹配的
                        m.invoke(mtt, arg2);           // IllegalArgumentException
                        break;
                    case 3:                             //new Object[0]创建了一个空的数组, 对应可变参数来说,相当于没有传入参数
                        m.invoke(mtt, new Object[0]);  // works
                        break;
                    case 4:
                        Object arg4 = new Object[0];  //new Object[0]存储在一个Object中,将Object对待,而原有的方法是没有参数的,将会不匹配
                        m.invoke(mtt, arg4);           // IllegalArgumentException
                        break;
                    default:
                        System.out.format("Test not found%n");
                }
            } catch (Exception x) {
                x.printStackTrace();
            }
        }
    }
    
    • 4.当反射调用失败的时候会抛出InvocationTargetException的异常
    /**
     * InvocationTargetException  包装了所有的非检测和检测的异常,当方法被调用的时候,可以用InvocationTargetException来抓取异常
     */
    public class MethodTroubleReturns {
        private void drinkMe(int liters) {
            if (liters < 0)
                throw new IllegalArgumentException("I can't drink a negative amount of liquid");
        }
    
        public static void main(String... args) {
            try {
                MethodTroubleReturns mtr = new MethodTroubleReturns();
                Class<?> c = mtr.getClass();
                Method m = c.getDeclaredMethod("drinkMe", int.class);
                m.invoke(mtr, -1);
            } catch (InvocationTargetException x) {
                Throwable cause = x.getCause();
                System.err.format("drinkMe() failed: %s%n", cause.getMessage());
            } catch (Exception x) {
                x.printStackTrace();
            }
        }
    }
    

    测试结果如下:

    drinkMe() failed: I can't drink a negative amount of liquid
    

    相关文章

      网友评论

          本文标题:(5) Method类的基本使用

          本文链接:https://www.haomeiwen.com/subject/nitcaftx.html