什么是反射,为什么有用?
什么是反射,为什么有用?
我对Java特别感兴趣,但是我认为所有语言的原理都是相同的。
解决方案
回答
名称反射用于描述能够检查同一系统(或者本身)中的其他代码的代码。
例如,假设我们在Java中有一个未知类型的对象,并且我们想在该对象上调用" doSomething"方法(如果存在)。除非对象符合已知的接口,否则Java的静态类型化系统并不是真正为支持该类型而设计的,但是使用反射,代码可以查看该对象并确定其是否具有名为" doSomething"的方法,然后在需要时调用它想要。
因此,给我们一个用Java编写的代码示例(假设有问题的对象是foo):
Method method = foo.getClass().getMethod("doSomething", null); method.invoke(foo, null);
Java中一种非常常见的用例是带注释的用法。例如,JUnit 4将使用反射来遍历类,以查找带有@Test批注的方法,然后在运行单元测试时调用它们。
有一些不错的反射示例,可入门:http://docs.oracle.com/javase/tutorial/reflect/index.html
最后,是的,在支持反射的其他静态类型语言(如C#)中,这些概念非常相似。在动态类型的语言中,上述用例不是必需的(因为编译器将允许在任何对象上调用任何方法,如果该方法不存在,则在运行时失败),但是第二种情况是查找标记或者以某种方式工作仍然很普遍。
来自评论的更新:
The ability to inspect the code in the system and see object types is not reflection, but rather Type Introspection. Reflection is then the ability to make modifications at runtime by making use of introspection. The distinction is necessary here as some languages support introspection, but do not support reflection. One such example is C++
回答
反射是一组功能,使我们可以访问程序的运行时信息并修改其行为(有一些限制)。
这很有用,因为它允许我们根据程序的元信息来更改运行时行为,即可以检查函数的返回类型并更改处理情况的方式。
例如,在Cfor中,我们可以在运行时加载程序集(.dll),然后对其进行检查,浏览类并根据找到的内容采取措施。它还允许我们在运行时创建类的实例,调用其方法等。
在哪里有用?并非每次都有用,但适用于具体情况。例如,我们可以使用它获取用于登录目的的类的名称,根据配置文件上指定的内容为事件动态创建处理程序,等等。
回答
并非每种语言都支持反射,但是支持它的语言的原理通常是相同的。
反射是"反射"程序结构的能力。或者更具体。要查看我们拥有的对象和类,并以编程方式获取有关它们实现的方法,字段和接口的信息。我们还可以查看注释等内容。
在许多情况下很有用。我们希望能够在任何地方将类动态地插入代码中。很多对象关系映射器都使用反射功能来实例化数据库中的对象,而无需事先知道它们将使用什么对象。插件体系结构是反射有用的另一个地方。在这些情况下,能够动态加载代码并确定其中是否存在实现用于插件的正确接口的类型很重要。
回答
Reflection is a language's ability to inspect and dynamically call classes, methods, attributes, etc. at runtime.
例如,Java中的所有对象都具有方法getClass(),即使我们在编译时不知道该对象的类(例如,如果我们将其声明为Object),也可以通过该方法来确定对象的类,这似乎微不足道,但是这种反映在动态性较差的语言(例如C ++)中是不可能的。更高级的用法使我们可以列出和调用方法,构造函数等。
反射很重要,因为它使我们可以编写在编译时不必"了解"所有程序的程序,从而使它们更具动态性,因为它们可以在运行时绑定在一起。可以针对已知接口编写代码,但是可以使用配置文件中的反射实例化要使用的实际类。
正因为如此,许多现代框架都广泛使用反射。大多数其他现代语言也都使用反射,在脚本语言(例如Python)中,它们甚至更加紧密地集成在一起,因为在这些语言的通用编程模型中感觉更为自然。
回答
反射是一种允许应用程序或者框架使用甚至可能尚未编写的代码的关键机制!
以典型web.xml文件为例。这将包含servlet元素列表,其中包含嵌套的servlet-class元素。 servlet容器将处理web.xml文件,并通过反射为每个servlet类创建新的新实例。
另一个示例是用于XML解析的Java API(JAXP)。通过众所周知的系统属性"插入" XML解析器提供程序的位置,该属性用于通过反射构造新实例。
最后,最全面的示例是Spring,它使用反射来创建其bean,并大量使用代理
回答
我最喜欢的反射用法之一是下面的Java转储方法。它使用任何对象作为参数,并使用Java反射API打印出每个字段名称和值。
import java.lang.reflect.Array; import java.lang.reflect.Field; public static String dump(Object o, int callCount) { callCount++; StringBuffer tabs = new StringBuffer(); for (int k = 0; k < callCount; k++) { tabs.append("\t"); } StringBuffer buffer = new StringBuffer(); Class oClass = o.getClass(); if (oClass.isArray()) { buffer.append("\n"); buffer.append(tabs.toString()); buffer.append("["); for (int i = 0; i < Array.getLength(o); i++) { if (i < 0) buffer.append(","); Object value = Array.get(o, i); if (value.getClass().isPrimitive() || value.getClass() == java.lang.Long.class || value.getClass() == java.lang.String.class || value.getClass() == java.lang.Integer.class || value.getClass() == java.lang.Boolean.class ) { buffer.append(value); } else { buffer.append(dump(value, callCount)); } } buffer.append(tabs.toString()); buffer.append("]\n"); } else { buffer.append("\n"); buffer.append(tabs.toString()); buffer.append("{\n"); while (oClass != null) { Field[] fields = oClass.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { buffer.append(tabs.toString()); fields[i].setAccessible(true); buffer.append(fields[i].getName()); buffer.append("="); try { Object value = fields[i].get(o); if (value != null) { if (value.getClass().isPrimitive() || value.getClass() == java.lang.Long.class || value.getClass() == java.lang.String.class || value.getClass() == java.lang.Integer.class || value.getClass() == java.lang.Boolean.class ) { buffer.append(value); } else { buffer.append(dump(value, callCount)); } } } catch (IllegalAccessException e) { buffer.append(e.getMessage()); } buffer.append("\n"); } oClass = oClass.getSuperclass(); } buffer.append(tabs.toString()); buffer.append("}\n"); } return buffer.toString(); }