反射とは何ですか
「リフレクションにより、JVMで実行されているプログラムが実行時間の動作を検出および変更できます。」この概念は、多くの場合、内省と混同されます。ウィキペディアのこれら2つの用語の説明は次のとおりです。
内省の例:オブジェクトが特定のクラスに属しているかどうかを検出するために使用されます。
if(obj instanceof dog){dog d =(dog)obj; D.BARK();}リフレクションの例:class.forname()メソッドは、クラスまたはインターフェイス(文字列または完全資格の名前)の名前を介して対応するクラスオブジェクトを取得できます。 ForNameメソッドは、クラスの初期化をトリガーします。
//リフレクションクラス<?
Javaでは、オブジェクトの構造を変更できないため、反射は内省に近いです。一部のAPIは、メソッドとプロパティの可視性を変更するために使用できますが、構造を変更することはできません。
配列の反映
配列の反射の使用は何ですか?アレイの反射はいつ使用する必要がありますか?次のコードを見てみましょう。
integer [] nums = {1、2、3、4}; object [] objs = nums; // integer []に自動的に変換される可能性があります[]オブジェクトobj = nums; // integer []はもちろんオブジェクトint [] ids = {1、2、3、4}です。 // object [] objs2 = ids; // int []をオブジェクト[]オブジェクトobj2 = idsに変換することはできません。 // int []はオブジェクトです上記の例は、基本的なタイプの1次元配列は、オブジェクトとしてではなく、オブジェクトとのみ見なすことができることを示しています[]。
int [] [] intarray = {{1、2}、{3、4}}; object [] oa = intarray;オブジェクトobj = intarray; // integer [] [] integerArray = intarray; int [] [] not integer [] [] [] [] [] [] integerArray2 = new Integer [] [] {{1、2}、{3、4}}; object [] [] oa2 = integerarray2; object [] oa3 = integerarray2;オブジェクトobj2 = integerarray2;上記の例から、Javaの2桁の配列がアレイの配列であることがわかります。配列を反映する例を見てみましょう。
パッケージCN.ZQ.Array.Reflect; java.lang.reflect.arrayをインポートします。 java.util.arraysをインポートします。 java.util.randomをインポートします。 public class arrayReflect {public static void main(string [] args){random rand = new Random(47); int [] is = new int [10]; for(int i = 0; i <is.length; i ++){is [i] = rand.nextint(100); } system.out.println(is); system.out.println(arrays.aslist(is)); /*上記の2つの出力は、「[[I@14318BB]」に似た文字列です。この文字列は、配列に保存されているコンテンツを表示できません。もちろん、トラバーサルを使用して、配列*/ system.out.println( "-1。印刷による配列へのトラバーサル - ")に内容を出力します。 for(int i = 0; i <is.length; i ++){system.out.print(is [i]+""); } system.out.println(); System.out.println( "-2。printing-印刷による配列へのトラバーサル - ");オブジェクトobj = is; // 1次元のint配列をObject System.out.println( "obj isarray:" + obj.getclass()。isarray()); for(int i = 0; i <array.getLength(obj); i ++){int num = array.getint(obj、i); //この一般的に使用される方法を使用して、対応するインデックス位置の値を取得することもできます// object value = array.get(obj、i); //配列が基本タイプを保存する場合、基本型system.out.print(num + "")に対応するラッパータイプ。 }}}出力:
[i@14318bb [[i@14318bb] -1。従来の手段で配列を通過して配列を印刷します-58 55 93 61 61 29 68 0 22 7 -2。配列をトラバースすることにより、配列をトラバースすることにより配列をトラバースすることによって配列を印刷します-OBJイサレイ:True 58 55 93 61 61 29 68 0 22 7
上記の例は、最初にintの1次元配列を作成し、次に0〜100の整数をランダムに埋めます。次に、system.out.println()メソッドを介して配列を直接出力するか、配列を使用します。配列はリストに変換されてから出力に変換され、通過は予想される出力結果ではありません。次に、配列内の内容は通常の配列トラバーサル方法で出力され、その後、int []をオブジェクトとして扱い、反射を使用してコンテンツを通過します。 class.isarray()を使用して、オブジェクトが配列であるかどうかを判断できます。アレイの場合、配列の関連情報は、配列を反映するツールクラスであるJava.lang.Refflt.Arrayを介して取得されます。このクラスでは、いくつかのGETメソッドを使用して配列の長さを取得します。各バージョンは、基本タイプの1次元配列の対応するインデックス、値(オブジェクト配列、INTインデックス)を取得する一般的な方法、値を設定する方法、およびアレイインスタンスを作成する2つの方法を取得するために使用されます。配列リフレクションツールクラスを通じて、特定の配列がどの基本的なタイプの配列であるかを判断せずに、一般的なコードを作成するための配列反射を簡単に使用できます。
パッケージCN.ZQ.Array.Reflect; java.lang.reflect.arrayをインポートします。 public class newArrayInstance {public static void main(string [] args){object o = array.newinstance(int.class、20); int [] is =(int [])o; System.out.println( "is.length =" + is.length);オブジェクトo2 = array.newinstance(int.class、10、8); int [] [] ISS =(int [] [])o2; system.out.println( "siss.length =" + siss.length + "、iss [0] .lenght =" + iss [0] .length); }} is.length = 20 iss.length = 10、iss [0] .lenght = 8配列は、配列を作成するための2つの方法を渡しました
Object NewInstance(class <?> componentType、int length)は、提供されたクラスに基づいて指定された長さの配列を作成します。 int.classが上記のように提供されている場合、長さは10で、これは新しいint [10]に相当します。
Object NewInstance(class <?> componentType、int ...寸法)は、提供されたクラスと寸法に基づいて配列を作成します。変数パラメーター寸法は、配列の各寸法の長さを指定するために使用されます。上記の例のように、新しいINT [10] [8]の2次元配列を作成することと同等ですが、各次元の長さが異なる多次元配列を作成することはできません。アレイを作成する最初の方法により、このような配列を作成できます。オブジェクトo = array.newinstance(int []。クラス、20)を使用して、2次元配列を作成できます。これは、オブジェクトo = new int [20] [];
もちろん、上記の例を使用して配列を作成することはまれですが、実際には冗長です。新しいを介して直接配列を作成してみませんか?リフレクションは、新しいものよりも速いだけでなく、書かれたプログラムも読みやすいものではないため、新しいものほど直接的ではありません。実際、反射を通して配列を作成することは本当にまれです。リフレクションを使用して配列を作成するために、どのような異常なニーズがありますか!
基本的なタイプの配列を出力するときにいくつかの障害が発生したため、以下はアレイリフレクションを使用してツールクラスを実装して目的の出力を実現します。
パッケージcn.zq.util; java.io.bytearrayoutputStreamをインポートします。 java.io.printStreamをインポートします。 java.lang.reflect.arrayをインポートします。 public class print {public static void print(object obj){print(obj、system.out); } public static void print(object obj、printstream out){out.println(getPrintString(obj)); } public static void println(){print(system.out); } public static void println(printstream out){out.println(); } public static void printnb(object obj){printnb(obj、system.out); } public static void printnb(object obj、printstream out){out.print(getPrintString(obj)); } public static PrintStream Format(String Format、object ... objects){return format(system.out、format、objects); } public static PrintStream Format(printStream out、string format、object ... objects){object [] handleObjects = new object [objects.length]; for(int i = 0; i <objects.length; i ++){object object = objects [i]; if(object == null || isprimitivewrapper(object)){handleObjects [i] = object; } else {bytearrayoutputStream bos = new bytearrayoutputStream(); printStream ps = new PrintStream(BOS); printnb(object、ps); ps.close(); handleObjects [i] = new String(bos.tobytearray()); }} out.format(format、handleObjects);戻ります。 } /***特定のオブジェクトが基本タイプのラッパークラスであるかどうかを判断します。 * @param o与えられたオブジェクトオブジェクト * @returnそれがプリミティブタイプのラッパークラスの場合、はい、それ以外の場合は返されます。 o booleanのインスタンス|| o文字のインスタンス|| oバイトのインスタンス|| o短い||のインスタンスo整数のインスタンス|| o longのインスタンス|| oフロートのインスタンス|| o doubleのインスタンス; } public static string getPrintString(object obj){stringbuilder result = new StringBuilder(); if(obj!= null && obj.getclass()。isarray()){result.append( "["); int len = array.getLength(obj); for(int i = 0; i <len; i ++){object value = array.get(obj、i); result.append(getPrintString(value)); if(i!= len -1){result.append( "、"); }} result.Append( "]"); } else {result.append(string.valueof(obj)); } return result.toString(); }}上記のプリントツールクラスは、出力用の実用的な静的方法を提供し、過負荷のバージョンを提供します。個人的な好みに基づいて過負荷のバージョンを記述することができ、基本的なタイプの1次元配列と多次元配列の印刷をサポートできます。印刷ツールテストの次の例を参照してください。
パッケージCN.ZQ.Array.Reflect; static cn.zq.util.print.printをインポートします。 java.io.printStreamをインポートします。 static cn.zq.util.print。*をインポートします。 public class printtest {static class person {private static int counter;プライベートファイナルInt = counter ++; public string toString(){return getClass()。getSimplename() + id; }} public static void main(string [] args)throws exception {print( " - print nonarray-"); print(new object()); print( " - 基本タイプの1次元配列を印刷 - "); int [] is = new int [] {1、22、31、44、21、33、65}; print(is); print( " - 基本タイプの2次元配列を印刷 - "); int [] [] ISS = new int [] [] {{11、12、13、14}、{21、22、}、{31、32、33}}; print(ISS); print( " - 非ベースタイプの1次元配列を印刷 - ");人[] persons = new Person [10]; for(int i = 0; i <persons.length; i ++){persons [i] = new Person(); } print(persons);印刷(人); print( "-2次元の非微小型タイプの配列を印刷 - "); person [] [] persons2 = new person [] [] {{new Person()}、{new Person()、new Person()}、{new Person()、new Person()、new Person()、new Person()、}; print(persons2); print( " - 空の配列を印刷 - "); print(new int [] {}); print( " - null値で配列を印刷 - "); objects = new object [] {new Person()、null、new object()、new Integer(100)}; print(objects); print( " - 特別なケース用の2次元配列を印刷 - "); object [] [] [] objects2 = new object [3] []; objects2 [0] = new object [] {}; object2 [2] = objects; print(objects2); print( " - 1次元配列の結果をファイルに出力 - "); printStream out = new PrintStream( "Out.C"); try {print(iss、out); }最後に{out.close(); } print( " - format output-"); format( "%-6d%s%b%s"、10086、 "is"、true、iss); /** *一般的に使用される印刷ツールクラスのいくつかの方法は上記にリストされています。 */}}出力:
-print nonArray-java.lang.object@61de33-プリントの基本タイプの1次元配列 - [1、22、31、44、21、33、65] - プリント2次元アレイの基本タイプ - [[11、12、13、14]、[21、22]、[31、32、32、32、32、32、32、32、32、32、32、32、32、32、32、32、32、32、32、32、32、32、33、33、32、32、32、32、32、32、32、32、32、32、32、32、32、32、32、32、32、33、33、33、33、33、33、33、33、32、32、33、32、32、33] person1、person2、person3、person4、person5、person6、person7、person8、person9] - プリント非ベースタイプの2次元配列 - [[person11、person12]、[person13、person14、person15]] -print空の配列 - [] - 特別な場合のプリント2次元配列 - [[]、null、[person16、null、java.lang.object@ca0b6、100]]] - 1次元アレイのファイルへのプリント---形式出力-10086は真[[11、12、13、14]、[21、32、33]、[31、32、33]
出力ファイル:
プリントツールクラスには、基本的なタイプの1次元配列と多次元配列を印刷する機能が既にあることがわかります。一般的に言えば、上記のツールクラスは非常に実用的であり、配列内のコンテンツを表示するたびに手動でコードを作成しないようにします。それは面倒すぎるでしょう。将来的には、印刷ツールクラスを使用してください。どれほど便利です。
上記のツールクラスは非常にうまく機能しますが、要件がある場合:配列(および他のコンテナ)を提供する場合は、リストを作成できます。それで、私たちは何をすべきですか?実際、Arrays.Aslistは常に予想される結果を得るとは限りません。 Java5はジェネリックを追加しますが、制限があり、C ++テンプレートほど一般的ではありません。それはまさにJavaに基本的なタイプがあるからです。自動ラッピングメカニズムがある場合でも、ジェネリックでは使用できません。パラメータータイプは、基本タイプではなく、特定のタイプである必要があります。これがあなた自身の解決策です:
パッケージcn.zq.util; java.lang.reflect.arrayをインポートします。 java.util.arraylistをインポートします。 java.util.arraysをインポートします。 java.util.numerationをインポートします。 java.util.iteratorをインポートします。 java.util.listをインポートします。 java.util.mapをインポートします。 public class CollectionUtils {public static list <?> aslist(object obj){return converttolist(makeTerator(obj)); } public static <t> list <t> converttolist(iterator <t> iterator){if(iterator == null){return null; } list <t> list = new ArrayList <T>(); while(iterator.hasnext()){list.add(iterator.next()); }返品リスト。 } @suppresswarnings({"rawTypes"、 "un -checked"})public static iterator <?> makeTerator(object obj){if(obj instanceof iterator){return(iterator <?>>)obj; } if(obj == null){return null; } if(obj instanceof map){obj =((map <?、?>)obj).entryset(); } iterator <?> iterator = null; if(obj instanceof iterable){iterator =((iterable <?>)obj).iterator(); } else if(obj.getclass()。isarray()){// object [] objs =(object [])obj; //プリミティブタイプの配列は、このarrayListリスト= new ArrayList(array.getLength(OBJ))のように変換できません。 for(int i = 0; i <array.getLength(obj); i ++){list.add(array.get(obj、i)); } iterator = list.iterator(); } else if(obj instanceof enumeration){iterator = new EnumerationIterator((eNumeration)obj); } else {iterator = arrays.aslist(obj).iterator(); } return iterator; } public static class enumerationiterator <t> Iterator <t> {private enumeration <t> enumeration; public enumerationiterator(列挙<t>列挙){this.enumeration = eNumeration; } public boolean hasnext(){return enumeration.hasmoreElements(); } public t next(){return enumeration.nextelement(); } public void remove(){新しいunsupportedoperationexception(); }}}テストコード:
パッケージCN.ZQ.Array.Reflect; java.util.iteratorをインポートします。 java.util.listをインポートします。 cn.zq.array.reflect.printtest.personをインポートします。 cn.zq.util.collectionutilsをインポートします。 Public Class CollectionUtilStest {public static void main(string [] args){system.out.println( "---基本タイプ1次元配列 - "); int [] nums = {1、3、5、7、9}; List <? System.out.println(list); System.out.println( " - 基本タイプではない1次元配列 - "); person [] persons = new person [] {new Person()、new Person()、new Person()、}; list <Person> personList =(list <person>)Collectutils.Aslist(persons); system.out.println(personlist); system.out.println(personlist); system.out.println( " - iterator-"); iterator <person> iterator = personlist.iterator(); List <Person> PersonList2 =(list <person>)Collectutils.Aslist(Iterator); System.out.println(personlist2); }}出力:
- 基本タイプの1次元配列 - [1、3、5、7、9] - 塩基タイプの1次元配列 - [Person0、Person1、Person2] -Iterator- [Person0、Person1、Person2]
Javaコンテナクラスライブラリでは、コレクション、マップ、および配列に分けることができます。イテレーター(および初期のレガシーインターフェイスの列挙)はすべてのコンテナの一般的なインターフェイスであり、コレクションインターフェイスはイテレーションからのものであるため(このインターフェイスのイテレーターはイテレーターを返します)、これらの状況はメンメイトレーター方法で1つずつ処理されます。マップタイプの場合、EntrySet()メソッドのみを呼び出す必要があります。 Iterableインターフェイス(コレクションを含む)を実装するクラスの場合は、Iterator()を呼び出して、Iteratorオブジェクトを直接取得します。列挙タイプには、適応のためにアダプターenumerationiteratorを使用します。配列の場合、配列反射を使用して配列を配列に移動し、array.aslist()メソッドを呼び出して、他のタイプのリストを作成します。 Collectutilsは、変換する他の方法も提供しており、必要に応じて必要な方法を追加できます。
概要:アレイリフレクションは、より面倒な判断ステートメントを記述しないように、アレイが表示される可能性のある設計のためのより便利で柔軟な方法を提供します。この柔軟性はパフォーマンスの価格を支払い、配列反射がまったく必要ない場合に配列反射を使用する必要はありません。配列反射を使用するかどうかは、実際の開発で異なります。ニーズに応じて配列反射を使用するかどうかを選択します。最良の方法は、練習を通して道を探求し、あなたが考える方法で書き、実際に常に改善することです。