リフレクションとは何ですか?なぜそれが役立つのですか?
私は特にJavaに興味がありますが、原則はどの言語でも同じだと思います。
@Jailbreak
ます。プライベートフィールドやメソッドなどへの直接のタイプセーフなアクセスを提供します。Javaコンパイラにコードを安全に検証させ、Manifoldに基になる反射アクセスを生成させます。詳細:Manifold.systems/docs.html#type-safe-reflection
リフレクションとは何ですか?なぜそれが役立つのですか?
私は特にJavaに興味がありますが、原則はどの言語でも同じだと思います。
@Jailbreak
ます。プライベートフィールドやメソッドなどへの直接のタイプセーフなアクセスを提供します。Javaコンパイラにコードを安全に検証させ、Manifoldに基になる反射アクセスを生成させます。詳細:Manifold.systems/docs.html#type-safe-reflection
回答:
リフレクションという名前は、同じシステム(またはそれ自体)内の他のコードを検査できるコードを記述するために使用されます。
たとえば、Javaに不明なタイプのオブジェクトがあり、存在する場合はそのオブジェクトに対して「doSomething」メソッドを呼び出したいとします。Javaの静的型付けシステムは、オブジェクトが既知のインターフェースに準拠していない限り、これをサポートするように実際に設計されていませんが、リフレクションを使用して、コードはオブジェクトを調べ、「doSomething」というメソッドがあるかどうかを確認し、をしたい。
したがって、Javaでこれのコード例を提供するには(問題のオブジェクトがfooであると想像してください):
Method method = foo.getClass().getMethod("doSomething", null);
method.invoke(foo, null);
Javaでの非常に一般的な使用例の1つは、注釈の使用です。たとえば、JUnit 4はリフレクションを使用して、@ Testアノテーションが付けられたメソッドのクラスを調べ、ユニットテストの実行時にそれらを呼び出します。
http://docs.oracle.com/javase/tutorial/reflect/index.htmlから始めるための良いリフレクションの例がいくつかあります。
そして最後に、はい、概念はリフレクションをサポートする他の静的に型付けされた言語(C#など)とほとんど同じです。動的に型付けされた言語では、上記のユースケースはそれほど必要ではありません(コンパイラーは任意のオブジェクトで任意のメソッドを呼び出すことができるため、存在しない場合は実行時に失敗します)。特定の方法で作業することはまだ一般的です。
コメントから更新:
システム内のコードを検査してオブジェクトタイプを確認する機能は、リフレクションではなく、タイプイントロスペクションです。リフレクションは、イントロスペクションを利用して実行時に変更を加える機能です。一部の言語はイントロスペクションをサポートしていますが、リフレクションをサポートしていないため、ここでは区別が必要です。そのような例の1つはC ++です。
Method
、Constructor
、Modifier
、Field
、Member
、基本的には明らか除くすべてClass
)内にあるjava.lang.*reflect*
パッケージ。おそらく、「リフレクション」という概念には、「型の内省」と実行時の変更の両方が包括的に含まれているのでしょうか。
リフレクションは、実行時にクラス、メソッド、属性などを検査して動的に呼び出す言語の機能です。
たとえば、JavaのすべてのオブジェクトにはメソッドgetClass()
があります。これは、コンパイル時にオブジェクトのクラスがわからない場合でも(たとえば、それをとして宣言した場合Object
)、簡単に見えるかもしれませんが、そのようなリフレクションは不可能です。以下のような少ないダイナミックな言語でC++
。より高度な使用法では、メソッドやコンストラクターなどをリストして呼び出すことができます。
リフレクションを使用すると、コンパイル時にすべてを「知る」必要がないプログラムを作成できるため、リフレクションが重要になります。リフレクションは、実行時に結合できるため、より動的になります。コードは既知のインターフェースに対して作成できますが、使用される実際のクラスは、構成ファイルからのリフレクションを使用してインスタンス化できます。
最新のフレームワークの多くは、まさにこの理由でリフレクションを広範囲に使用しています。他のほとんどの最近の言語もリフレクションを使用しており、スクリプト言語(Pythonなど)では、それらの言語の一般的なプログラミングモデル内でより自然に感じられるため、より緊密に統合されています。
リフレクションの私のお気に入りの使用法の1つは、以下の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();
}
callCount
はゼロに設定する必要があります。その値は、出力の各行の前にいくつのタブを置くかを決定するために使用されます。ダンプが「サブオブジェクト」をダンプする必要があるたびに、出力は親にネストされたものとして印刷されます。その方法は、別の方法でラップすると便利です。考えてくださいprintDump(Object obj){ System.out.println(dump(obj, 0)); }
。
リフレクションの使用
リフレクションは、Java仮想マシンで実行されているアプリケーションの実行時の動作を検査または変更する機能を必要とするプログラムで一般的に使用されます。これは比較的高度な機能であり、言語の基本をよく理解している開発者のみが使用する必要があります。その警告を念頭に置いて、リフレクションは強力な手法であり、アプリケーションが他の方法では不可能な操作を実行できるようにすることができます。
拡張機能
アプリケーションは、完全修飾名を使用して拡張性オブジェクトのインスタンスを作成することにより、外部のユーザー定義クラスを利用できます。クラスブラウザーとビジュアル開発環境クラスブラウザーは、クラスのメンバーを列挙できる必要があります。ビジュアル開発環境では、型情報をリフレクションで利用できるため、開発者が正しいコードを作成するのに役立ちます。デバッガーとテストツールデバッガーは、クラスのプライベートメンバーを検査できる必要があります。テストハーネスはリフレクションを利用して、クラスで定義された検出可能なセットAPIを体系的に呼び出し、テストスイートで高レベルのコードカバレッジを確保できます。
リフレクションの欠点
リフレクションは強力ですが、無差別に使用しないでください。リフレクションを使用せずに操作を実行できる場合は、使用しないことをお勧めします。リフレクションを介してコードにアクセスする場合は、次の点に注意してください。
リフレクションには動的に解決されるタイプが含まれるため、特定のJava仮想マシンの最適化は実行できません。したがって、リフレクティブ操作は非リフレクティブ操作よりもパフォーマンスが遅いため、パフォーマンスに敏感なアプリケーションで頻繁に呼び出されるコードのセクションでは避けてください。
リフレクションにはランタイムパーミッションが必要ですが、セキュリティマネージャーで実行している場合は、このパーミッションが存在しない可能性があります。これは、アプレットなどの制限されたセキュリティコンテキストで実行する必要があるコードの重要な考慮事項です。
リフレクションを使用すると、プライベートフィールドやメソッドへのアクセスなど、非リフレクトコードでは違法となる操作をコードで実行できるため、リフレクションを使用すると予期しない副作用が発生し、コードが機能しなくなり、移植性が損なわれる可能性があります。リフレクティブコードは抽象化を壊すため、プラットフォームのアップグレードにより動作が変わる可能性があります。
ソース:リフレクションAPI
リフレクションは、アプリケーションまたはフレームワークがまだ作成されていないコードを処理できるようにするための重要なメカニズムです。
たとえば、典型的なweb.xmlファイルを見てみましょう。これには、ネストされたサーブレットクラス要素を含むサーブレット要素のリストが含まれます。サーブレットコンテナはweb.xmlファイルを処理し、リフレクションによって各サーブレットクラスの新しい新しいインスタンスを作成します。
もう1つの例は、Java API for XML Parsing (JAXP)です。XMLパーサープロバイダーが、リフレクションを通じて新しいインスタンスを構築するために使用される既知のシステムプロパティを介して「プラグイン」されている場合。
そして最後に、最も包括的な例は、リフレクションを使用してBeanを作成し、プロキシを多用するSpringです。
すべての言語がリフレクションをサポートするわけではありませんが、原則は通常、リフレクションをサポートする言語で同じです。
リフレクションとは、プログラムの構造を「反映」する機能です。またはもっと具体的に。所有しているオブジェクトとクラスを確認し、それらが実装するメソッド、フィールド、およびインターフェースに関する情報をプログラムで取得します。アノテーションなども確認できます。
多くの状況で役に立ちます。クラスをコードに動的にプラグインできるようにしたいすべての場所。多くのオブジェクトリレーショナルマッパーはリフレクションを使用して、使用するオブジェクトを事前に知らなくても、データベースからオブジェクトをインスタンス化できます。プラグインアーキテクチャは、リフレクションが役立つ別の場所です。これらの状況では、コードを動的にロードして、プラグインとして使用する適切なインターフェースを実装するタイプがあるかどうかを判別できることが重要です。
リフレクションを使用すると、新しいオブジェクトのインスタンス化、メソッドの呼び出し、実行時に動的にクラス変数のget / set操作を行うことができ、その実装に関する事前の知識は必要ありません。
Class myObjectClass = MyObject.class;
Method[] method = myObjectClass.getMethods();
//Here the method takes a string parameter if there is no param, put null.
Method method = aClass.getMethod("method_name", String.class);
Object returnValue = method.invoke(null, "parameter-value1");
上記の例では、nullパラメータは、メソッドを呼び出すオブジェクトです。メソッドが静的な場合はnullを指定します。メソッドが静的でない場合、呼び出し中にnullではなく有効なMyObjectインスタンスを提供する必要があります。
リフレクションでは、クラスのプライベートメンバー/メソッドにアクセスすることもできます。
public class A{
private String str= null;
public A(String str) {
this.str= str;
}
}
。
A obj= new A("Some value");
Field privateStringField = A.class.getDeclaredField("privateString");
//Turn off access check for this field
privateStringField.setAccessible(true);
String fieldValue = (String) privateStringField.get(obj);
System.out.println("fieldValue = " + fieldValue);
java.lang.reflect
)をインポートする必要はありません。クラスメタデータには、からアクセスできますjava.lang.Class
。リフレクションは非常に強力なAPIですが、実行時にすべてのタイプを解決するため、過度に使用するとアプリケーションの速度が低下する可能性があります。
Javaリフレクションは非常に強力で、非常に便利です。Javaリフレクションを使用すると、コンパイル時にクラス、メソッドなどの名前を知らなくても、実行時にクラス、インターフェース、フィールド、およびメソッドを検査できます。リフレクションを使用して、新しいオブジェクトをインスタンス化し、メソッドを呼び出し、フィールド値を取得/設定することもできます。
リフレクションの使用がどのように見えるかを示す簡単なJavaリフレクションの例:
Method[] methods = MyObject.class.getMethods();
for(Method method : methods){
System.out.println("method = " + method.getName());
}
この例では、MyObjectというクラスからClassオブジェクトを取得します。この例では、クラスオブジェクトを使用して、そのクラスのメソッドのリストを取得し、メソッドを反復処理して名前を出力します。
編集:ほぼ1年後、Reflectionについて読んでいる間、Reflectionの使用例がほとんどないため、この回答を編集しています。
<bean id="someID" class="com.example.Foo">
<property name="someField" value="someValue" />
</bean>
Springコンテキストがこの<bean>要素を処理するとき、引数「com.example.Foo」を指定してClass.forName(String)を使用し、そのクラスをインスタンス化します。
次に、リフレクションを使用して<property>要素の適切なセッターを取得し、その値を指定された値に設定します。
プライベートメソッドの場合
Method method = targetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);
プライベートフィールドの場合
Field field = targetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
例:
たとえば、APIメソッドを使用して取得したオブジェクトをアプリケーションに提供するリモートアプリケーションを考えてみましょう。オブジェクトに基づいて、何らかの計算を実行する必要があるかもしれません。
プロバイダーは、オブジェクトが3つのタイプであることを保証し、オブジェクトのタイプに基づいて計算を実行する必要があります。
したがって、それぞれが異なるロジックを含む3つのクラスで実装する可能性があります。当然、オブジェクト情報は実行時に利用できるため、静的に計算を実行するようにコーディングできないため、リフレクションを使用して、プロバイダーから受信したオブジェクト。
instanceof
実行時にオブジェクトタイプを判別するために使用できませんか?
反射の簡単な例。チェスのゲームでは、実行時にユーザーが何を動かすかはわかりません。リフレクションは、実行時に既に実装されているメソッドを呼び出すために使用できます。
public class Test {
public void firstMoveChoice(){
System.out.println("First Move");
}
public void secondMOveChoice(){
System.out.println("Second Move");
}
public void thirdMoveChoice(){
System.out.println("Third Move");
}
public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Test test = new Test();
Method[] method = test.getClass().getMethods();
//firstMoveChoice
method[0].invoke(test, null);
//secondMoveChoice
method[1].invoke(test, null);
//thirdMoveChoice
method[2].invoke(test, null);
}
}
リフレクションは、実行時にメソッド、クラス、インターフェースの動作を検査または変更するために使用されるAPI です。
java.lang.reflect package
ます。java.lang
そしてjava.lang.reflect
パッケージはJavaのリフレクションのためのクラスを提供します。
リフレクションは、についての情報を取得するために使用できます–
クラスこのgetClass()
メソッドは、オブジェクトが属するクラスの名前を取得するために使用されます。
コンストラクタこのgetConstructors()
メソッドは、オブジェクトが属するクラスのパブリックコンストラクターを取得するために使用されます。
メソッドザ・getMethods()
メソッドは、オブジェクトが属するクラスのpublicメソッドを取得するために使用されます。
の リフレクションAPIは、主に使用されます。
IDE(統合開発環境)例:Eclipse、MyEclipse、NetBeansなど。
デバッガやテストツールなど。
リフレクションを使用する利点:
拡張機能:アプリケーションは、完全修飾名を使用して拡張オブジェクトのインスタンスを作成することにより、外部のユーザー定義クラスを利用できます。
デバッグおよびテストツール:デバッガーはリフレクションのプロパティを使用して、クラスのプライベートメンバーを調べます。
欠点:
パフォーマンスのオーバーヘッド:リフレクティブ操作は、非リフレクティブ操作よりもパフォーマンスが遅いため、パフォーマンスに敏感なアプリケーションで頻繁に呼び出されるコードのセクションでは避けてください。
内部の露出:リフレクティブコードは抽象化を壊すため、プラットフォームのアップグレードにより動作が変わる可能性があります。
私の理解に従って:
リフレクションを使用すると、プログラマはプログラム内のエンティティに動的にアクセスできます。つまり、プログラマーがクラスやそのメソッドについて知らない場合にアプリケーションをコーディングするときに、リフレクションを使用することにより、そのようなクラスを動的に(実行時に)利用できます。
これは、クラス名が頻繁に変更されるシナリオで頻繁に使用されます。このような状況が発生した場合、プログラマーがアプリケーションを書き直してクラスの名前を何度も変更するのは複雑です。
代わりに、リフレクションを使用することで、クラス名が変更される可能性を心配する必要があります。
リフレクションは、プログラムのランタイム情報にアクセスし、その動作を(いくつかの制限付きで)変更できるようにする一連の関数です。
プログラムのメタ情報に応じて実行時の動作を変更できるため、つまり、関数の戻り値の型を確認し、状況の処理方法を変更できるため、便利です。
たとえばC#では、実行時にアセンブリ(.dll)を読み込み、それを調べて、クラスを移動し、見つけたものに従ってアクションを実行できます。また、実行時にクラスのインスタンスを作成したり、そのメソッドを呼び出したりすることもできます。
どこで役に立ちますか?毎回役立つわけではありませんが、具体的な状況で役立ちます。たとえば、ロギングの目的でクラスの名前を取得したり、構成ファイルでの指定に従ってイベントのハンドラーを動的に作成したりするために使用できます。
記載されているすべてにポイントを追加したいだけです。
リフレクションAPIあなたは、ユニバーサル書くことができますtoString()
任意のオブジェクトのための方法を。
デバッグ時に役立ちます。
ここにいくつかの例があります:
class ObjectAnalyzer {
private ArrayList<Object> visited = new ArrayList<Object>();
/**
* Converts an object to a string representation that lists all fields.
* @param obj an object
* @return a string with the object's class name and all field names and
* values
*/
public String toString(Object obj) {
if (obj == null) return "null";
if (visited.contains(obj)) return "...";
visited.add(obj);
Class cl = obj.getClass();
if (cl == String.class) return (String) obj;
if (cl.isArray()) {
String r = cl.getComponentType() + "[]{";
for (int i = 0; i < Array.getLength(obj); i++) {
if (i > 0) r += ",";
Object val = Array.get(obj, i);
if (cl.getComponentType().isPrimitive()) r += val;
else r += toString(val);
}
return r + "}";
}
String r = cl.getName();
// inspect the fields of this class and all superclasses
do {
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
// get the names and values of all fields
for (Field f : fields) {
if (!Modifier.isStatic(f.getModifiers())) {
if (!r.endsWith("[")) r += ",";
r += f.getName() + "=";
try {
Class t = f.getType();
Object val = f.get(obj);
if (t.isPrimitive()) r += val;
else r += toString(val);
} catch (Exception e) {
e.printStackTrace();
}
}
}
r += "]";
cl = cl.getSuperclass();
} while (cl != null);
return r;
}
}
反射は、オブジェクトにその外観を表示させることです。この議論は反射とは何の関係もないようです。実際、これは「自己識別」能力です。
リフレクション自体は、JavaやC#のような自己認識と自己検知の機能に欠ける言語を表す言葉です。彼らは自己認識の能力を持っていないので、それがどのように見えるかを観察したいとき、それがどのように見えるかを反映するために別のものが必要です。RubyやPythonなどの優れた動的言語は、他の人の助けを借りずに、自分の言語の反映を認識することができます。Javaのオブジェクトは、リフレクションクラスのオブジェクトであるミラーなしではどのように見えるかを認識できませんが、Pythonのオブジェクトでは、ミラーなしでオブジェクトを認識できます。そのため、Javaでのリフレクションが必要です。
Javaドキュメントページから
java.lang.reflect
パッケージは、クラスとオブジェクトに関する反映情報を取得するためのクラスとインターフェースを提供します。リフレクションを使用すると、ロードされたクラスのフィールド、メソッド、コンストラクターに関する情報にプログラムでアクセスでき、リフレクトされたフィールド、メソッド、コンストラクターを使用して、セキュリティ制限内で対応するフィールドを操作できます。
AccessibleObject
必要に応じてアクセスチェックを抑制ReflectPermission
できます。
一緒に、このパッケージのクラス、java.lang.Class
などなど、デバッガ、インタプリタ、オブジェクトインスペクタ、クラスブラウザ、およびサービスなどのアプリケーションに対応Object Serialization
してしJavaBeans
(実行時のクラスに基づく)ターゲットオブジェクトのパブリックメンバーまたはメンバーのいずれかにその必要性へのアクセスがによって宣言与えられたクラス
次の機能が含まれています。
によって公開されているメソッドについては、このドキュメントのリンクをご覧ください。Class
クラス。
この記事(Sosnoski Software Solutions、Inc、社長、Dennis Sosnoski)とこの記事から(security-explorations pdf)から:
Reflectionを使用するよりもかなりの欠点があることがわかります
リフレクションのユーザー:
リフレクションの欠点:
一般的な虐待:
リフレクション機能の悪用に関するこのSEの質問をご覧ください。
Javaでプライベートフィールドを読み取るにはどうすればよいですか?
概要:
システムコード内からその機能を安全に使用しないと、Javaセキュリティモードの侵害につながる可能性があります。したがって、この機能は慎重に使用してください
Woozle
、起動時にクラスに他のクラスを調べて、静的RegisterAsWoozleHelper()
メソッドがあるクラスを確認し、見つかったすべてのメソッドを、Woozle
自分自身に知らせるために使用できるコールバックで呼び出すことを避けます。たとえば、データをデシリアライズするときにReflectionを使用する必要があります。
名前自体が示唆するように、実行時に動的にインスタンスを作成するメソッドを呼び出す機能を提供することを除いて、それは例のクラスメソッドなどが持つ内容を反映しています。
実際にコードを知らなくてもサービスを呼び出すために、多くのフレームワークや木の下のアプリケーションで使用されています。
Reflection
多くの用途があります。私がよく知っているのは、オンザフライでコードを作成できることです。
IE:動的クラス、関数、コンストラクター-任意のデータに基づく(xml / array / sql results / hardcoded / etc。)
この質問に例を挙げて回答したいと思います。最初に、Hibernate
プロジェクトはステートメントReflection API
を生成するために使用してCRUD
、実行中のアプリケーションと永続ストアの間の裂け目を橋渡しします。ドメインで状況が変化した場合、はHibernate
それらを認識して、それらをデータストアに永続化する必要があります。
代わりに動作しLombok Project
ます。コンパイル時にコードを挿入するだけで、ドメインクラスにコードが挿入されます。(ゲッターとセッターは問題ないと思います)
Hibernate
reflection
これは、アプリケーションのビルドプロセスへの影響が最小限であるために選択しました。
そしてJava 7 MethodHandles
以降、として機能しReflection API
ます。プロジェクトでは、ロガーを操作するために、次のコードをコピーして貼り付けます。
Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass().getName());
この場合、タイプミスをするのは難しいからです。
私は例で説明するのが最善であり、答えのどれもそれを行うようには見えないので...
リフレクションを使用する実用的な例は、Javaで記述されたJava言語サーバーまたはPHPで記述されたPHP言語サーバーなどです。言語サーバーは、オートコンプリート、定義へのジャンプ、コンテキストヘルプ、ヒントタイプなどのIDE機能を提供します。入力時にすべての可能な一致を表示するためにすべてのタグ名(オートコンプリート可能な単語)を表示するには、Language Serverがdocブロックとプライベートメンバーを含むクラスに関するすべてを検査する必要があります。そのためには、このクラスの反映が必要です。
別の例は、プライベートメソッドの単体テストです。そのための1つの方法は、リフレクションを作成し、テストのセットアップフェーズでメソッドのスコープをpublicに変更することです。もちろん、プライベートメソッドを直接テストすべきではないと主張することもできますが、それは重要ではありません。