Dosen't Reflection API打破了数据封装的目的吗?

时间:2013-05-19 13:13:26

标签: java reflection private encapsulation

最近我遇到了Reflection API,令我惊讶的是我们可以访问甚至更改私有变量。我尝试了以下代码

import java.lang.reflect.Field;

public class SomeClass{
    private String name = "John";
}

public class Test{
    public static void main(String args[]) throws Exception {
        SomeClass myClass = new SomeClass();

        Field fs = myClass.getClass().getDeclaredField("name");
        fs.setAccessible(true);

        System.out.println("Variable is " + fs.getName() + " and value is "
                + fs.get(myClass));

        fs.set(myClass, "Sam");
        System.out.println("Variable is " + fs.getName() + " and value is "
                + fs.get(myClass));
    }
}

我得到了以下输出。

Variable is name and value is John
Variable is name and value is Sam

我们说Java是一种面向对象的语言,它的主要功能是数据封装,继承,多态......等不是反射API改变了数据封装的目的吗?为什么我们必须使用Reflection API?我在一些网站上读到它可以用于测试目的,但据我说,模块已经过测试,可以使用JUnit测试用例轻松完成。所以任何人都可以解释为什么我们有这样的黑客攻击?

4 个答案:

答案 0 :(得分:9)

  

反射API是不是改变了数据封装的目的?

是和否。

  • 是的,反射API 的一些用法可以打破数据封装。
  • 不,并非所有使用反射API 执行都会破坏数据封装。实际上,一个明智的程序员只有在有充分理由这样做时才能通过反射API打破封装。
  • 不,反射API不会更改数据封装的目的。数据封装的目的保持不变......即使有人故意破坏它。
  

为什么我们必须使用Reflection API?

很多使用反射不要打破封装;例如使用反射来找出类的超类型,它有什么注释,它有什么成员,调用可访问的方法和构造函数,读取和更新可访问的字段等等。

有些情况可以接受(在不同程度上)使用封装破坏各种反射:

  • 您可能需要查看封装类型(例如访问/修改私有字段)作为实现某些单元测试的最简单方法(或唯一方法)。

  • 某些形式的依赖注入(也称为IoC),序列化和持久性需要访问和/或更新私有字段。

  • 偶尔,您需要打破封装以解决某些无法修复的类中的错误。

  

我在一些网站上读到它可以用于测试目的,但据我说,模块已经过测试,可以使用JUnit测试用例轻松完成。所以任何人都可以解释为什么我们有这样的黑客攻击?

这取决于你班级的设计。设计为可测试的类将是可测试的,无需访问“私有”状态,或者将公开该状态(例如protected getters)以允许测试。如果类没有这样做,那么JUnit测试可能需要使用反射来查看抽象内部。

这是不可取的(IMO),但如果您正在为某人编写的类编写单元测试,并且您无法“调整”API以提高可测试性,那么您可能必须选择是否使用反射完全测试。


最重要的是,数据封装是我们努力实现的理想(在Java中),但有些情况下,实际上正确的做法是打破它或忽略它。

请注意,并非所有OO语言都支持像Java那样强大的数据封装。例如,Python和Javascript都是无可争议的OO语言,但它们都使一个类可以轻松访问和修改另一个类的对象状态......甚至可以更改其他类的行为。强大的数据抽象并不是每个人对面向对象意味着什么的看法的核心。

答案 1 :(得分:0)

是的,它确实违反了OO概念。但是它不会破坏任何Java安全模型。如有必要,它可以由java安全管理器控制。 Java反射本身是一件有用的事情。它在Annotations和IOC中使用,它们是非常有用的概念,能够在运行时处理类。

答案 2 :(得分:0)

您不能将EncapsulationPolymorphism与反射混合。

Reflection的目的完全不同。使用反射,您可以动态创建类型并执行它。您可以在运行时动态访问类的成员。


例如,最近我在我的应用程序中使用了反射运行Plugins。我从特定目录加载了dll,然后通过反射将其转换为共享接口来创建类的对象。

答案 3 :(得分:0)

  

反射API是不是改变了数据的目的   封装

不要太依赖规则。有时候打破它们是有用的。

Reflextion非常有用。以我的代码为例,记录查找更改,以便我可以将它们记录到数据库中:

public static void log(String userID, Object bOld, Object bNew)
          throws IntrospectionException, IllegalAccessException, InvocationTargetException {
      String res = "";//String to hold the change record
      boolean changed = false;
      try {
        if(bOld != null){ //if this is an update
            BeanInfo beanInfo = Introspector.getBeanInfo(bOld.getClass());
            res = bOld.getClass().getSimpleName() + " - ";
            //loop and compare old values with new values and add them to our string if they are changed
            for (PropertyDescriptor prop : beanInfo.getPropertyDescriptors()) {
                Method getter = prop.getReadMethod();
                Object vOld = getter.invoke(bOld); //old value
                Object vNew = getter.invoke(bNew); //new value
                if (vOld == vNew || (vOld != null && vOld.equals(vNew))) {
                  continue;
                }
                changed = true;
                res = res + "(" + prop.getName()  + ", " +  vOld  + ", " + vNew + ")";
            }
          }

这样做更容易。如果我使用getter,我将不得不为每个类编写一个单独的方法,并在每次添加新字段时进行修改。通过反射,我可以编写一个方法来处理我的所有类。我写了关于记录更改的方法here