65.9K
CodeProject 正在变化。 阅读更多。
Home

Java泛型代码 - 反射,轻松搞定

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.86/5 (7投票s)

2004年4月10日

CPOL

4分钟阅读

viewsIcon

55776

downloadIcon

1244

使用这个工具类,让Java的反射变得更容易。

引言

在你担任软件设计师/程序员的职业生涯中,你可能会遇到在运行时使用对象的需求,而对它们在设计时了解不多。Java通过反射来实现这一点。

Java泛型代码 - 轻松处理反射

如果你曾使用过JDBC,那么下面的代码会让你觉得熟悉

public Connection getConnection(String driverClass, String dbUrl, String userName, 
                                String passWord) {
    Connection connection = null;
    try {
        if (Class.forName(driverClass) != null) {
            connection = DriverManager.getConnection(dbURL, userName, passWord);
            if (connection != null) {
                connection.setAutoCommit(false);
            }
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return connection;
}

如果你写过类似的代码,你就用过反射。

它究竟是什么?

反射是一组API,Java使用它们来解决各种问题。例如,使用IDE设计GUI涉及操作大量控件,将它们添加到框架中并调整它们的属性。你的IDE(如果用Java编写)会使用反射来加载类,确定它们有哪些属性,让你修改这些属性,并调用对象中的适当方法来将属性更改为你选择的值。

我们能用它做什么?

如你所知,运行时对象必须被加载,并且拥有字段和方法。因此,反射主要关注三件事:

  1. 当你只知道类名而在运行时加载类。
  2. 在不知道对象字段名称或类型的情况下访问它们的字段。
  3. 调用这些匿名对象的方

为什么它很困难?

熟悉Java的反射API可能是一项挑战,尤其是在时间紧迫的情况下。因此,我将我关于反射的大部分知识收集到一个易于使用的工具类中。我将通过一个演示程序简要介绍这个类。

演示

演示程序非常简单:我将尝试按其完全限定名(即包名+类名)加载一个类,然后通过调用一个我仅知道名称的方法来操作它。

演示代码的类结构如下:

  • dev.easyref.tester: DemoApplication - 演示的主类。
  • dev.easyref.data : Employee - 一个简单的我将在运行时加载的数据类。
  • dev.easyref.util: Arguments - 用于执行所有反射工作的工具类。

下面的Employee类仅包含信息

public class Employee {
  // These are the data-members, notice that we can never access them externally..
  private int age;
  private float salary;
  private String firstName;
  private String lastName;

  public Employee(String firstName, String lastName, int age, float salary) {
    this.age = age;
    this.salary = salary;
    this.firstName = firstName;
    this.lastName = lastName;
  }

  public void updateSalary(float increase) {
    salary += increase;
  }

  public String toString() {
    return "<Employee Name: [" + firstName + ", " + lastName + "], Age: [" + age + "], "
           "Salary: [" + salary + "]>";
  }
}

演示代码按如下方式操作Employee类

 1: import dev.easyref.util.*;

 2: public class DemoApplication {
 3:   public static void main(String[] args) {
 4:   // Create an Arguments object..
 5:     Arguments xArgs = new Arguments();
 6:     // Fill the Arguments object with data..
 7:     xArgs.addElement("Doron");
 8:     xArgs.addElement("Barak");
 9:     xArgs.addElement(36);
10:     xArgs.addElement(3000.0f);
11:     // Create an instance of an Employee class based on the data..
12:     Object o = xArgs.getInstance("dev.easyref.data.Employee");
13:     // Show the object that we received..
14:     System.out.println("Created instance >>   " + o);
15:     // Create a new Arguments object, collecting the data in the object..
16:     xArgs = new Arguments(o);
17:     // Display the collected data..
18:     System.out.println("Instace data >>       " + xArgs);
19:     // Change the first stored value to 1000..
20:     xArgs.setElementAt(1000.0f, 1);
21:     // Order the Arguments object to not print Excpetion stacks..
22:     xArgs.hideExceptions();
23:     // Invoke the updateSalary() method of the Employee instance..
24:     xArgs.runMethod(o, "updateSalary");
25:     // Check to see if we had an Excpetion..
26:     if (xArgs.hadMethodError()) {
27:       // Remove unwanted values from the Arguments object..
28:       xArgs.removeElementAt(0);
29:       xArgs.removeElementAt(1);
30:       xArgs.removeElementAt(1);
31:       // Now, we try to invoke updateSalary() again..
32:       xArgs.runMethod(o, "updateSalary");
33:     }
34:     // Display object after Salary raise..
35:     System.out.println("After Salary Raise >> " + o);
36:   }
37: }

第5-10行
从第5-10行开始,创建一个Arguments对象,并向其中添加数据。数据的添加顺序与使用Employee类的构造函数时出现的顺序相同。我的Arguments对象继承自Java的Vector对象,以便于操作其数据元素。

第12-14行
在这里,Arguments对象用于获取dev.easyref.data.Employee类的一个实例,然后显示结果。Arguments对象的getInstance()方法完成了调用Class.forName()以及与正确调用请求类的构造函数进行协商的所有工作。

第16-18行
现在使用Arguments对象收集Employee实例中的所有数据,然后显示Arguments对象。请注意,收集到Arguments对象中的数据在元素顺序和类型上与Employee类中声明的数据成员相匹配。

第20-24行
使用从Vector类继承的setElementAt()方法,将浮点值1,000设置为Arguments对象的第一个槽。接下来,Arguments对象被命令隐藏所有运行时异常,并尝试调用Employee实例的updateSalary()方法。你可能已经猜到,我隐藏异常的原因是因为此时调用该方法会导致NoSuchMethodException,因为Arguments对象当前包含的数据与updateSalary()方法的签名不匹配。

第26-35行
可以查询Arguments对象以查看最后一次方法调用是否导致了错误。由于我们知道确实发生了错误,所以我们现在移除不需要的数据,并尝试重新调用updateSalary()方法,这次使用正确的值。为了证明薪资已正确提高,我再次显示Employee实例。

演示应用程序的输出应该如下所示:
创建实例 >> <Employee Name: [Doron, Barak], Age: [36], Salary: [3000.0]>
实例数据 >> [36, 3000.0, Doron, Barak]
错误:找不到类[dev.easyref.data.Employee]的方法 {
  updateSalary(int p0, float p1, String p2, String p3);
}
调薪后 >> <Employee Name: [Doron, Barak], Age: [36], Salary: [4000.0]>

你可能已经注意到,尽管Arguments对象没有显示异常,但它确实报告了方法调用的不幸结果,并将错误消息发送到了System.err流。

结论

下载代码,尽情使用吧。Java的反射是一个非常重要的工具,没有它,许多Java的特性将根本不存在。没有反射,你就无法使用复杂的IDE来设计GUI。你也无法使用序列化或RMI。

你可以在你的应用程序中使用Arguments类本身,我只希望我有时间来正确地记录它。

© . All rights reserved.