扫码关注官方订阅号
为什么需要反射,这不是破坏了封装吗?
走同样的路,发现不同的人生
1.为什么需要反射?反射(reflection)允许静态语言在运行时(runtime)检查、修改程序的结构与行为。 在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误;换句话说,程序在运行时的行为都是固定的。如果想在运行时改变,就需要反射这东西了。举个例子: 在Spring中,有这样的java bean配置:
静态语言
运行时(runtime)
<bean id="someID" class="net.liujiacai.Foobar"> <property name="someField" value="someValue" /> </bean>
spring在处理这个bean标签时,发现class属性指定的是net.liujiacai.Foobar这个类,就会调用Class.forName(String)来实例化这个类,再通过反射,可以取到someField属性的值了。 如果我们想改变这个程序运行时的信息,我们这里直接修改bean,property的属性即可,无需重新编译。
net.liujiacai.Foobar
Class.forName(String)
someField
bean
property
在动态语言中,使用变量不需要声明类型,因而不需要这反射这种机制。 比如在javascript中,我们知道有个变量foobar,不管foobar有没有sayHello()属性,我们都可以这么写:
foobar
sayHello()
foobar.sayHello()
因为没有类型检查,这里这么写是允许的。至于在运行时报不报错,就要看运行时foobar的真正值了。
2.反射是不是破坏了封装性? 答案可以说是,也可以说不是。 说是,是因为,通过运用反射机制API,确实可以访问到一个对象的私有成员。 说不是,是因为,并不是所有的反射API,都破坏了封装性。即使因某种必要原因,访问了私有成员,封装的目的还是不变的。比如,在Java种,你想让hello与hi等价。也就是说让"hello".equals("hi") == true,你可以这么做:
hello
hi
"hello".equals("hi") == true
Field value = String.class.getDeclaredField("value"); value.setAccessible(true); value.set("hello", "hi".toCharArray()); System.out.println("hello".equals("hi")); //输出true
其实真正的封装是种理想的状态,不见得是面向对象中的银弹。现实中,有些场合也许破坏封装性是种更明智的选择。
参考: - Java Reflection Tutorial - Dosen't Reflection API break the very purpose of Data encapsulation?
我认为主要是方便编写具有更好用的 API 的类库。
举个例子,多数 PHP 路由框架会允许你提供一个对象,这个对象中的每个 public 的非 static 方法都会对应到一个 url 上,例如 app->use('/account', new Account());, 如果 Account 中有个 login 方法,那么会被对应到 /account/login, 如果 PHP 不提供任何形式的 反射,那么是无法实现这样的功能的。
app->use('/account', new Account());
login
/account/login
再比如多数 PHP 的 ORM 允许你像读写对象属性一样地修改数据库记录。当然,这个 功能 在 PHP 里不叫反射,但也是通过赋予程序在运行时获取和修改编译时(在传统的面向对象概念里,属性应该是编译时确定的)数据的方式,便于程序(主要是库)设计出更好的 API.
反射和「魔术方法」之类的技术的确会降低代码被静态分析的可能性,尤其是对于静态类型的语言,例如在 PHP 里(当然,PHP 不算静态类型)如果你使用了魔术方法,就很难得到 IDE 的代码补全和语法检查了。
所以我认为反射和类似的功能只应当被用在类库的设计中,而且一定要经过充分的测试,滥用这些特征会写出难以维护和漏洞百出的代码。
微信扫码关注PHP中文网服务号
QQ扫码加入技术交流群
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
PHP学习
技术支持
返回顶部
1.为什么需要反射?
反射(reflection)允许
静态语言在运行时(runtime)检查、修改程序的结构与行为。在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误;换句话说,程序在运行时的行为都是固定的。如果想在运行时改变,就需要反射这东西了。举个例子:
在Spring中,有这样的java bean配置:
spring在处理这个bean标签时,发现class属性指定的是
net.liujiacai.Foobar这个类,就会调用Class.forName(String)来实例化这个类,再通过反射,可以取到someField属性的值了。如果我们想改变这个程序运行时的信息,我们这里直接修改
bean,property的属性即可,无需重新编译。在动态语言中,使用变量不需要声明类型,因而不需要这反射这种机制。
比如在javascript中,我们知道有个变量
foobar,不管foobar有没有sayHello()属性,我们都可以这么写:因为没有类型检查,这里这么写是允许的。至于在运行时报不报错,就要看运行时foobar的真正值了。
2.反射是不是破坏了封装性?
答案可以说是,也可以说不是。
说是,是因为,通过运用反射机制API,确实可以访问到一个对象的私有成员。
说不是,是因为,并不是所有的反射API,都破坏了封装性。即使因某种必要原因,访问了私有成员,封装的目的还是不变的。比如,在Java种,你想让
hello与hi等价。也就是说让"hello".equals("hi") == true,你可以这么做:其实真正的封装是种理想的状态,不见得是面向对象中的银弹。现实中,有些场合也许破坏封装性是种更明智的选择。
参考:
- Java Reflection Tutorial
- Dosen't Reflection API break the very purpose of Data encapsulation?
我认为主要是方便编写具有更好用的 API 的类库。
举个例子,多数 PHP 路由框架会允许你提供一个对象,这个对象中的每个 public 的非 static 方法都会对应到一个 url 上,例如
app->use('/account', new Account());, 如果 Account 中有个login方法,那么会被对应到/account/login, 如果 PHP 不提供任何形式的 反射,那么是无法实现这样的功能的。再比如多数 PHP 的 ORM 允许你像读写对象属性一样地修改数据库记录。当然,这个 功能 在 PHP 里不叫反射,但也是通过赋予程序在运行时获取和修改编译时(在传统的面向对象概念里,属性应该是编译时确定的)数据的方式,便于程序(主要是库)设计出更好的 API.
反射和「魔术方法」之类的技术的确会降低代码被静态分析的可能性,尤其是对于静态类型的语言,例如在 PHP 里(当然,PHP 不算静态类型)如果你使用了魔术方法,就很难得到 IDE 的代码补全和语法检查了。
所以我认为反射和类似的功能只应当被用在类库的设计中,而且一定要经过充分的测试,滥用这些特征会写出难以维护和漏洞百出的代码。