Answers:
反射比仅按其名称调用方法要慢得多,因为反射必须检查字节码中的元数据,而不是仅使用预编译的地址和常量。
反射功能也更强大:您可以检索a protected
或final
member 的定义,删除保护并对其进行操作,就好像它已声明为可变的一样!显然,这破坏了该语言通常为您的程序提供的许多保证,并且可能非常非常危险。
这几乎解释了何时使用它。通常不。如果要调用方法,只需调用它即可。如果要变异成员,只需将其声明为可变成员即可,而不要落后于编译后台。
反射在现实世界中的一种有用用法是在编写必须与用户定义的类进行互操作的框架时,框架作者不知道成员(甚至是类)将是什么。反思使他们无需事先知道就可以处理任何课程。例如,我认为没有反射就不可能编写一个复杂的面向方面的库。
再举一个例子,JUnit过去使用了一些琐碎的反思:它枚举了类中的所有方法,假定所有被调用testXXX
的方法都是测试方法,并且仅执行那些方法。但这现在可以通过注释来更好地完成,实际上,JUnit 4基本上已经转移到了注释。
我曾经像你一样,对反射了解不多-仍然不了解-但我确实使用过一次。
我有一个带有两个内部类的类,每个类都有很多方法。
我需要调用内部类中的所有方法,而手动调用它们将耗费大量精力。
使用反射,我可以仅用2-3行代码来调用所有这些方法,而不用调用方法本身的数量。
我将反射的使用分为三组:
反射允许程序处理可能不存在的代码,并以可靠的方式进行处理。
“普通代码”具有一些片段,例如片段URLConnection c = null
,由于它们的绝对存在,它们导致类加载器加载URLConnection类,作为加载此类的一部分,并引发ClassNotFound异常并退出。
通过反射,您可以在启动依赖于它们的实际类之前,根据它们的名称以字符串形式加载类,并测试它们的各种属性(可用于控件外部的多个版本)。一个典型的示例是用于使Java程序在OS X下看起来本机的OS X特定代码,该代码在其他平台上不存在。
从根本上讲,反射意味着将程序的代码用作数据。
因此,当程序代码是有用的数据源时,使用反射可能是一个好主意。(但是需要权衡取舍,因此可能并不总是一个好主意。)
例如,考虑一个简单的类:
public class Foo {
public int value;
public string anotherValue;
}
并且您想从中生成XML。您可以编写代码以生成XML:
public XmlNode generateXml(Foo foo) {
XmlElement root = new XmlElement("Foo");
XmlElement valueElement = new XmlElement("value");
valueElement.add(new XmlText(Integer.toString(foo.value)));
root.add(valueElement);
XmlElement anotherValueElement = new XmlElement("anotherValue");
anotherValueElement.add(new XmlText(foo.anotherValue));
root.add(anotherValueElement);
return root;
}
但这是很多样板代码,每次更改类时,都必须更新代码。真的,您可以描述这段代码的作用
这是一种算法,算法的输入是类:我们需要它的名称,以及它的属性的名称,类型和值。这就是反射的来源:它使您可以访问此信息。Java允许您使用Class
类的方法检查类型。
其他一些用例:
但是,全反射不仅意味着查看现有代码(其本身被称为“自省”),而且还意味着修改或生成代码。Java中有两个主要的用例:代理和模拟。
假设您有一个接口:
public interface Froobnicator {
void froobnicateFruits(List<Fruit> fruits);
void froobnicateFuel(Fuel fuel);
// lots of other things to froobnicate
}
并且您有一个实现一些有趣的实现:
public class PowerFroobnicator implements Froobnicator {
// awesome implementations
}
实际上,您还有第二种实现:
public class EnergySaverFroobnicator implements Froobnicator {
// efficient implementations
}
现在,您还需要一些日志输出;只要调用方法,您只需要一条日志消息。您可以将日志输出显式地添加到每个方法中,但这会很烦人,并且您必须执行两次。每个实现一次。(添加更多实现时,更多。)
相反,您可以编写代理:
public class LoggingFroobnicator implements Froobnicator {
private Logger logger;
private Froobnicator inner;
// constructor that sets those two
public void froobnicateFruits(List<Fruit> fruits) {
logger.logDebug("froobnicateFruits called");
inner.froobnicateFruits(fruits);
}
public void froobnicateFuel(Fuel fuel) {
logger.logDebug("froobnicateFuel( called");
inner.froobnicateFuel(fuel);
}
// lots of other things to froobnicate
}
同样,有一种重复的模式可以用算法描述:
该算法的输入是接口定义。
反射允许您使用此算法定义一个新类。Java允许您使用java.lang.reflect.Proxy
类的方法来执行此操作,并且有些库为您提供了更多功能。
那么反思的不利之处是什么?