ChinaUnix.net
 >> ChinaUnix.net > Java

又遇上怪事——invoke和getMethod

作者:Eisen     发表时间:2002/10/30 02:21pm

写了个小测试程序如下。

package test;

/**
* <p>Title: </p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2002</p>
* <p>Company: </p>
* @author unascribed
* @version 1.0
*/
import java.lang.reflect.*;

public class methodtest
{

       public methodtest()
       {
       }
       public static void main(String[] args)
       {
               methodtest methodtest1 = new methodtest();
methodtest1.run();
       }

public void run()
{
Class ms ;
try
{
ms = Class.forName("test.MS") ;
Method[] mds = ms.getMethods() ;
for (int i=0;i<mds.length ;i++)
{
System.out.println(""+i+":"+mds[i].getName() );
};
if (mds[0].getName().equals("out") ) System.out.println("right");
else System.out.println("false");
/*mds[0].invoke(ms,null);
Method m = ms.getClass() .getMethod("out",null) ;
Class[] cs=m.getParameterTypes() ;
System.out.println("length="+cs.length) ;
for (int i=0;i<cs.length ;i++)
{
System.out.println(cs[i].getName() );
};
m.invoke(ms,null) ;
*/}catch(Exception e)
{
e.printStackTrace() ;
};
}
}

class MS
{
public MS()
{
}

public void out()
{
System.out.println("MS out is invoked");
}
}


此文章相关评论:
该文章有13个相关评论如下:(点这儿可以发表评论)
Eisen 发表于: 2002/10/30 02:22pm
这时得到的输出结果如下:
0:out

1:hashCode

2:getClass

3:wait

4:wait

5:wait

6:equals

7:toString

8:notify

9:notifyAll

right

 
Eisen 发表于: 2002/10/30 02:25pm
[这个贴子最后由Eisen在 2002/10/30 04:22pm 编辑]

很明显,out这个函数已经被发现了。
于是,我在run()这个函数中又加上一句,成了
public void run()
{
Class ms ;
try
{
ms = Class.forName("test.MS") ;
Method[] mds = ms.getMethods() ;
for (int i=0;i<mds.length ;i++)
{
System.out.println(""+i+":"+mds[i].getName() );
};
if (mds[0].getName().equals("out") ) System.out.println("right");
else System.out.println("false");
[color=red]Method m = ms.getClass() .getMethod("out",null) ;[/color]
}catch(Exception e)
{
e.printStackTrace() ;
};
}
结果成了这么一个错误:
java.lang.NoSuchMethodException: java.lang.Class.out()

 
Eisen 发表于: 2002/10/30 02:29pm
[这个贴子最后由Eisen在 2002/10/30 02:30pm 编辑]

于是,我想想算了,改改吧,又改成了——
public void run()
{
Class ms ;
try
{
ms = Class.forName("test.MS") ;
Method[] mds = ms.getMethods() ;
for (int i=0;i<mds.length ;i++)
{
System.out.println(""+i+":"+mds[i].getName() );
};
if (mds[0].getName().equals("out") ) System.out.println("right");
else System.out.println("false");
[color=red]mds[0].invoke(ms,null);[/color]
}catch(Exception e)
{
e.printStackTrace() ;
};
}
结果错得更离谱:
java.lang.IllegalArgumentException: object is not an instance of declaring class

这是怎么一回事?谁能说说看吗?

 
cinc 发表于: 2002/10/30 04:14pm
java.lang.NoSuchMethodException: java.lang.Class.out()
这句话的意思应该是 java.lang.Class 没有 out() 方法,只有 MS 才有。

所以
Method m = ms.getClass() .getMethod("out",null) ;
改成:
Method m = ((MS)ms.getClass()).getMethod("out",null) ;

不知可以不可以

 
Eisen 发表于: 2002/10/30 04:22pm
是啊……我试过了。但是 MS和Class之间不能互相cast type的。
java里没有强类型转换啊……这要是C++就好了。呵呵。随便我指针怎么指法。
 
cinc 发表于: 2002/10/30 04:29pm
看看这里的例子:
http://java.sun.com/docs/books/tutorial/reflect/object/invoke.html
:)
 
Eisen 发表于: 2002/10/30 04:41pm
哦……是这样的——那个例子和我的比起来差别在这里——
我如果写成这样就和它一样了。
ms = new MS();
Method m = ms.getClass() .getMethod("out",null) ;
m.invoke(ms,null) ;
但是,这不是我所希望达到的效果,我希望的是不但能够动态的引用method,而且也能动态地加载类。所以我开始用的是 Class.forName()来生成ms,而不是直接new MS()。
这样其实也是有目的的。例如,我想写一个界面的话,界面需要依据不同的应用进行调整,某一块区域这次放panel,下次可能就放dialog。这些都是靠配置文件来配属的,而不能直接写死在程序里。
所以,要用Class.forName()来动态生成实例,然后再动态引用其中各自不同的操作method。这就与上例有了不同了。那个例子是已经固定了类对象,再引用那个类对象中的method实例。而我需要的是动态的对象和动态的method……
这有点象DCOM了,不过还是差得远。
 
sakulagi 发表于: 2002/10/30 10:12pm
手头没有环境没法尝试,不过我觉得是不是可以使用Factory Pattern来解决你遇到的这个问题。
 
cinc 发表于: 2002/10/30 10:20pm
[quote][b]下面引用由[u]sakulagi[/u]在 [i]2002/10/30 10:12pm[/i] 发表的内容:[/b]
手头没有环境没法尝试,不过我觉得是不是可以使用Factory Pattern来解决你遇到的这个问题。
[/quote]

呵呵,我也考虑过这个问题,在 Factory 里,输入类的名字的字符串,返回这个类的一个实例。

这里有个例子:

http://java.sun.com/docs/books/tutorial/reflect/object/noarg.html

import java.lang.reflect.*;
import java.awt.*;

class SampleNoArg {

  public static void main(String[] args) {
     Rectangle r = (Rectangle) createObject("java.awt.Rectangle");
     System.out.println(r.toString());
  }

  static Object createObject(String className) {
     Object object = null;
     try {
         Class classDefinition = Class.forName(className);
         object = classDefinition.newInstance();
     } catch (InstantiationException e) {
         System.out.println(e);
     } catch (IllegalAccessException e) {
         System.out.println(e);
     } catch (ClassNotFoundException e) {
         System.out.println(e);
     }
     return object;
  }
}

Java 的 reflect 功能蛮有意思的。
:)

 
Eisen 发表于: 2002/10/31 12:04pm
嗯。是啊。问题的解决关键就在这个newInstance()的使用上了。
不过这样也只能部分解决问题,因为newInstance()生成了一个新的实例,但是很多时候需要把当前对象,通过父类引用传递给其他的对象加以引用,而这时有些又往往因为Java不支持Class的多重继承和强类型转换而无法实现了。
这个问题,一时间我自己也没想清楚,以后理清了头绪再出来和大家探讨。
 
hahu 发表于: 2002/10/31 12:08pm
学习!!
但是不太懂,:S
 
pilgram 发表于: 2002/11/02 12:32pm
because the method out() is not a static method ,so it belongs to  one instance of the class ,doesn't belong to the class.
ms is one Class instance.you can call it by mds[i].invoke(ms.newInstance(),null);
if you change the out() to a static method as following
public static out(){ .....}
then you can use   :  
if(mds[i].getName() == "out")
{
mds[i].invoke(ms,null);
}

all the source code is following ,
package Test;

import java.lang.reflect.*;

public class MethodTest
{

      public MethodTest()
      {
      }
      public static void main(String[] args)
      {
              MethodTest MethodTest1 = new MethodTest();
              MethodTest1.run();
      }

      public void run()
      {
          Class ms ;
          try
          {
              ms = Class.forName("Test.MS") ;
              Method[] mds = ms.getMethods() ;
              MS m = (MS)ms.newInstance() ;


              for (int i=0;i<mds.length ;i++)
              {
                  System.out.println(""+i+":"+mds[i].getName() );
                  if (mds[i].getName().equals("out") )
                  {
                      //由于out不是静态的,所以调用的时候必须有该类的实体
                      mds[i].invoke(m,null);
                  }
                  else if(mds[i].getName().equals("staticOut") )
                  {
                      //由于staticOut是静态的,调用它可以使用Class对象,也可以使用该类的实体
                      mds[i].invoke(mds[i],null) ;
                      mds[i].invoke(m,null);
                  }
              }
              }catch(Exception ex)
              {
                  ex.printStackTrace();
              }
      }
}

class MS
{
   public MS()
   {
   }

   public void out()
   {
       System.out.println("MS out is invoked");
   }

   static public void staticOut()
   {
       System.out.println("MS staticOut is invoked");
   }

}

 
Eisen 发表于: 2002/11/04 01:58pm
[quote][b]下面引用由[u]pilgram[/u]在 [i]2002/11/02 12:32pm[/i] 发表的内容:[/b]
because the method out() is not a static method ,so it belongs to  one instance of the class ,doesn't belong to the class.
ms is one Class instance.you can call it by mds.invoke(ms.newInstance(),n ...
[/quote]
呵呵。先谢谢了。不过……
这个首先用newInstance()的方法也就是
MS ms = new MS();
ms.out();
而后面把staticout()声明为static的方法也就是
MS.staticout();
这样在Java中都是可行的。但是在实际应用上有这样的问题存在——也就是我前面所提到的应用对象不一致的问题。
譬如说吧。我首先调用staticout()对某个MS对象进行了设置,后来,又在什么地方又需要调用staticout()对这个MS对象再次进行设置。这样就有问题了。不过,肯定又会人说为什么不把这个对象通过Factory直接返回static的呢。呵呵。这个也是一种办法,但是这样就要求一旦这个MS变化,或撤换的时候,Factory这个类也要重写过。这对于程序的扩展性来说不大对劲了。
 
 

Copyright © ChinaUnix.net  *  转载请注明出处及作者