黑马程序员技术交流社区

标题: 自己试验的反射中invoke解包问题 [打印本页]

作者: Teemo_Mann    时间: 2014-4-24 16:03
标题: 自己试验的反射中invoke解包问题
看了张孝祥老师的 23_对接收数组参数的成员方法进行反射 这一节,关于在调用invoke时参数会自动解包的问题,注:此篇文档是个人见解加个人实验结果,大家可以提出不同意见,共同进步
视频里只讲解了Main方法里的(String[] args)函数,我在想为什么会拆包?到底有哪些都会自动拆包呢,自己写的类,基础类型,还有一些Java提供的类都会吗?
带着这些疑问,首先看API
1.为什么会自动拆包
public Object invoke(Object obj,                     Object... args)              throws IllegalAccessException,                     IllegalArgumentException,                     InvocationTargetException对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。 如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。
如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。

我们注意到个别参数被自动解包,再看上面invoke的参数(object obj,Object... args)注意这里是Object,其实也就不难理解为什么会自动解包了
因为Java1.5开始有可变参数的特性,当你放入一个String[] args的时候,那么编译器怎么知道你是要放入一个String[]对象,还是多个String对象呢.
所以编译器默认就把它进行解包,解成一个个String对象以便于基本形参相匹配了.
所以说我们在传入参数的时候可以用(Object)new String[]("123,"456","789")告诉编译器我只传一个Object,
另外一种就是骗过编译器new Object[](new String[]("123","456","789"))编译器拆了之后还是一个String[]数组.

2.哪些会自动拆包
我实验的结果是,除了基本数据类型数组,其他统统要拆包,废话不说上代码
  1. import java.lang.reflect.Method;
  2. import java.lang.Class;
  3. import java.lang.Exception;
  4. class MainReflect
  5. {
  6.         public static void main(String[] args) throws Exception{
  7.                 Method method1 = Class.forName("IntReflect").getMethod("intParams",int[].class);
  8.                 Method method2 = Class.forName("StringReflect").getMethod("strParams",String[].class);
  9.                 Method method3 = Class.forName("IntegerReflect").getMethod("integerParams",Integer[].class);
  10.                 Method method4 = Class.forName("ObjectReflect").getMethod("objParams",NewObject[].class);
  11.                 /*第一个invoke编译时系统是不会提示的,运行时也不会报错,正常输出123",<font color="#ff0000">所以基本数据类型的数组是不解包的</font>*/
  12.                 method1.invoke(null,new int[]{1,2,3});
  13.                 /*下面三个编译时
  14.                 MainReflect.java:12: 警告:最后一个参数使用了不准确的变量类型的 varargs 方法的非
  15.                 varargs 调用;对于 varargs 调用,应使用 java.lang.Object对于非 varargs 调用,
  16.                 应使用 java.lang.Object[],这样也可以抑制此警告
  17.                 其实看到这个提示我们就该明朗,我们用的是varargs调用,应该使用java.lang.Object,系统认不清
  18.                 你的参数

  19.                 我们再看编译时错误
  20.                 Exception in thread "main" java.lang.IllegalArgumentException: wrong number of a
  21.                 rguments
  22.                 因为你只需要一个Integer对象[]对象,而你的方法拆成了三个Integer,不合法的参数异常.
  23.                 */
  24.                 method2.invoke(null,new String[]{"111","222","333"});
  25.                 method3.invoke(null,new Integer[]{10,20,30});
  26.                 method4.invoke(null,new NewObject[]{new NewObject(),new NewObject()}); //<font color="#ff0000">自定义的类一样自动解包</font>
  27.                
  28.         }
  29. }

  30. class IntReflect
  31. {
  32.         public static void intParams(int[] args){
  33.                 for(int i : args){
  34.                         System.out.println(i);
  35.                 }
  36.         }
  37. }

  38. class IntegerReflect
  39. {
  40.         public static void integerParams(Integer[] args){
  41.                 for(Integer i : args){
  42.                         System.out.println(i);
  43.                 }
  44.         }
  45. }

  46. class StringReflect
  47. {
  48.         public static void strParams(String[] args){
  49.                 for(String str : args){
  50.                         System.out.println(str);
  51.                 }
  52.         }
  53. }

  54. class ObjectReflect
  55. {
  56.         public static void objParams(NewObject[] objs){
  57.                 for(NewObject obj : objs){
  58.                         obj.open();       
  59.                 }
  60.         }
  61. }

  62. class NewObject
  63. {
  64.         public void open(){
  65.                 System.out.println("hello");
  66.         }
  67. }
复制代码
好了,以上是我的总结,欢迎大家讨论.






作者: 许庭洲    时间: 2014-4-24 21:04
值得学习ing!




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2