Javaプログラミング思想(十五)——タイプ情報の反射


讲完class、Classの後、続けます.
1)汎化Class参照
Classは汎用型を加えることもでき、加えた後にタイプチェックを行います.
本に原話を貼って、ClassClassより優れています.彼らは等価ですが、Classの利点は、たまたままたは不適切なクラス参照が使用されたことです.この非具体とは何なのか分かりません.
後で分かったけど、実はワイルドカードとしては未知で、直接結論を書くと具体的なタイプを書くことはできないでしょうが、著者の意味は、汎用的なClassを加えることで非具体的なバージョンを選んだということです.
汎用型を入れるのはコンパイル中のタイプチェックを提供するため、操作ミスをするとエラーが表示されますが、普通のClassを使うときは、実行するまで待たなければなりません.
また、このClass、JDKドキュメントでは、T - the type of the class modeled by this Class object. For example, the type of String.class is Class<String> . Use Class<?> if the class being modeled is unknown.
は汎用クラスを使用する宣言です.次の内容が含まれます.

Interface List<E>


Type Parameters: E - the type of elements in this list

同じく声明です.
2)転換文法
SE 5で追加されたClass参照変換の構文.
class Gun{
    int price = 1;
}
class DeathGun extends Gun{
    int price = 2;
}
public class TestCast {
    public static void main(String[] args) {
        Gun g = new DeathGun();
        Class<DeathGun> d = DeathGun.class;
        //    
        //Class<DeathGun> dd = Gun.class;  
        DeathGun gg = d.cast(g);
        System.out.println(gg.price);
    }
}

cast :
public T cast(Object obj) {
    if (obj != null && !isInstance(obj))
       throw new ClassCastException(cannotCastMsg(obj));
    return (T) obj;
}

3)タイプ変換前のチェック
if(x instanceof TV){
  ((TV)x).show();
}

オブジェクトタイプをチェックし、ダウンシフト時にエラーが発生した場合はClassCastExceptionを放出するので、まずinstanceOfを使用します.
InstaceOfとClassの等価性.
class Father{}
class Son extends Father{}
public class Test {
    static void  test(Object o ){
        System.out.println("type: "+o.getClass());
        System.out.println("o instanceof Father: "+(o instanceof Father));
        System.out.println("o instanceof Son: "+(o instanceof Son));
        System.out.println("Father.isInstanceOf(o)"+Father.class.isInstance(o));
        System.out.println("Son.isInstanceOf(o)"+Son.class.isInstance(o));
        System.out.println("o.getClass()==Father.class:"+(o.getClass()==Father.class));
        System.out.println("o.getClass()==Son.class:"+(o.getClass()==Son.class));
        System.out.println("o.getClass().equals(Father.class):"+(o.getClass().equals(Father.class)));
        System.out.println("o.getClass().equals(Son.class):"+(o.getClass().equals(Son.class)));
    }
    
    public static void main(String[] args) {
        test(new Father());
        test(new Son());
    }
}

result:
type: class son.Father
o instanceof Father: true
o instanceof Son: false
Father.isInstanceOf(o)true
Son.isInstanceOf(o)false
o.getClass()==Father.class:true
o.getClass()==Son.class:false
o.getClass().equals(Father.class):true
o.getClass().equals(Son.class):false

type: class son.Son
o instanceof Father: true
o instanceof Son: true
Father.isInstanceOf(o)true
Son.isInstanceOf(o)true
o.getClass()==Father.class:false
o.getClass()==Son.class:true
o.getClass().equals(Father.class):false
o.getClass().equals(Son.class):true

instanceofとはあなたがこのクラスを指しますか?あなたはこのクラスの派生クラスですか.
4)反射
本の上で言うのはとても簡単で、3ページぐらいです.
RTTI、ランタイムタイプ情報は、オブジェクトの特定のタイプを示すことができますが、このタイプは、RTTIが認識できるように、コンパイル時に既知である必要があります.すなわち,コンパイラは,RTTIによって処理されるクラスを知る必要がある.
これは制限されていないように見えますが、大規模なプログラミングに身を置くと、コンパイル時にプログラムがこのオブジェクトが属するクラスを知ることができない可能性があります.
RTTIと反射の本当の違いは、RTTI、コンパイラがコンパイル時にチェックして開くことだけです.classファイル、反射に対して、.classファイルのコンパイル時に取得できないのは、実行時に開いてチェックすることです.classファイル.
直接本の例を使って、コードを貼ります.
public class ShowMethods {
  private static String usage =
    "usage:
" +     "ShowMethods qualified.class.name
" +     "To show all methods in class or:
" +     "ShowMethods qualified.class.name word
" +     "To search for methods involving 'word'";   private static Pattern p = Pattern.compile("\\w+\\.");   public static void main(String[] args) {     if(args.length < 1) {       System.out.println(usage);       System.exit(0);     }     int lines = 0;     try {       Class<?> c = Class.forName(args[0]);       Method[] methods = c.getMethods();       Constructor[] ctors = c.getConstructors();       if(args.length == 1) {         for(Method method : methods)           System.out.println(             p.matcher(method.toString()).replaceAll(""));         for(Constructor ctor : ctors)           System.out.println(p.matcher(ctor.toString()).replaceAll(""));         lines = methods.length + ctors.length;       } else { // ,method , java ShowMethods ShowMethods ShowMethods // , args[1] ShowMethods, ShowMethods , main 。         for(Method method : methods)           if(method.toString().indexOf(args[1]) != -1) {             System.out.println(               p.matcher(method.toString()).replaceAll(""));             lines++;           }         for(Constructor ctor : ctors)           if(ctor.toString().indexOf(args[1]) != -1) {             System.out.println(p.matcher(               ctor.toString()).replaceAll(""));             lines++;           }       }     } catch(ClassNotFoundException e) {       System.out.println("No such class: " + e);     }   } } F:\>java ShowMethods ShowMethods public static void main(String[]) public final native Class getClass() public native int hashCode() public boolean equals(Object) public String toString() public final native void notify() public final native void notifyAll() public final native void wait(long) throws InterruptedException public final void wait(long,int) throws InterruptedException public final void wait() throws InterruptedException public ShowMethods()

MatcherのreplaceAllメソッドをゆっくり見ると、StringのreplaceAllとは異なり、後者には2つのパラメータがあり、1つの置換内容、1つは置換される内容である.前者にはパラメータが1つしかありません.
public String replaceAll(String replacement)

Replaces every subsequence of the input sequence that matches the pattern with the given replacement string.
public class TestString {  
    public static void main(String[] args) {  
        String s = "0898((*(*))(";
        // Pattern
        Pattern p = Pattern.compile("\\W");
        //p.matcher Matcher ,Matcher replaceAll 
        // 。
        System.out.println(p.matcher(s).replaceAll("-"));
    }  
}  
result:0898--------

正規表現の置換がない場合、結果は次のとおりです.
public static void ShowMethods.main(java.lang.String[])
public final native java.lang.Class java.lang.Object.getClass()
public native int java.lang.Object.hashCode()
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
public final native void java.lang.Object.wait(long) throws java.lang.Interrupte
dException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedEx
ception
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public ShowMethods()

実はクラスの前のパッケージ名を置き換えます.
forNameの結果はコンパイル時に不明であり、実行時にClassパラメータに渡されたことがわかりましたか.最初はこんな高級なものは使いませんでしたが、後で深く勉強してみるとSpringのようなものが使われていることに気づきました.
getMethodsは,自身,親から継承,インタフェースから実現されるpublicメソッドを含むクラスのすべての共有メソッドを手に入れた.
もう一つはgetDeclaredMethodsが手に入れたのはクラス自身が宣言する方法で、public方法だけでなくprotectedも手に入れることができて、privateさえも手に入れることができます.(最初は私も疑問に思っていましたが、パッケージを強調していたのではないでしょうか.私有は人に見られないのではないでしょうか.privateのものを直接手に入れることができますか.実はこのように見ることができます.何かに度がありますが、この度は塵も破れないわけではありません.一定の裏口があります).
getConstructorsはpublicのコンストラクタを返します.試してみると、プライベートなコンストラクタは表示されません.本ではここまで話すと止まり、拡張を続けます.
以前Java反射チュートリアルで紹介されたのを見たことがあります.
一、未知のメソッド呼び出し
public class TestRefl {
    public static void main(String[] args) {
        UnKnown uk = new UnKnown();
        Method m = null;
        try {
            m = uk.getClass().getMethod("print", new Class<?>[0]);
                m.invoke(uk);
        } catch (NoSuchMethodException | InvocationTargetException| SecurityException  |IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

class UnKnown{
    public void print(){
        System.out.println("haha");
    }
}

前に私はわざわざ方法をprivateに変えて、方法が見つからないことに気づいて、publicに変えるしかありません.
二、オブジェクトの作成
public class TestRefl {
    public static void main(String[] args) {
       Class c = null;
       try {
        c = c.forName("son.UnKnown");
        System.out.println(c.getName());
    } catch (ClassNotFoundException e) {
        System.out.println("not found");
    }
       try {
        UnKnown u = (UnKnown) c.newInstance();
        u.print();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    }
}

class UnKnown{
   static  void print(){
        System.out.println("haha");
    }
}

コンストラクション関数を使用してオブジェクトを取得します.
public class TestRefl {
    public static void main(String[] args) {
        Class c = null;
        try {
            c = c.forName("son.UnKnown");
        } catch (ClassNotFoundException e) {
            System.out.println("not found");
        }
        Constructor<?> ct[] = c.getConstructors();
        try {
            UnKnown u = (UnKnown) ct[0].newInstance();
            UnKnown u2 = (UnKnown) ct[1].newInstance(1);
            u.print();
            u2.print();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

class UnKnown {
    public UnKnown() {
    }

    public UnKnown(int i) {
    }

    static void print() {
        System.out.println("haha");
    }
}

コンストラクタ配列には順序があり,0は無パラメータのコンストラクタであり,1は伝参であり,方法では直接伝参すればよい.もちろん私有的な構造方法は手に入らない.
RTTIと反射の違いを説明すると、逆に反射の役割を説明することができます.つまり、実行時にオブジェクトのタイプをチェックし、任意にオブジェクトを呼び出す方法(Springとservletで使用されていますが、注意していません.xmlで構成し、属性はxmlに基づいて注入されます)であり、方法パラメータと属性を知ることができます.
反射はやはり強く、これからダイナミックエージェントを紹介します.以前虐待されたデザインモデルです.