JAva列挙タイプ分析

9679 ワード

最近androidの開発をして、列挙値を使う必要があります.このように連続的に値を割り当てることができます.私は前のc++のように書きます.以下に示します.
public enum ColorSelect {
           RED_BAGE = 0,
           GREEN_BAGE,
           BLUE_BAGE;
    }

コンパイルできません.
付与文を変更した後、次のようにします.
public enum ColorSelect {
           RED_BAGE ,
           GREEN_BAGE,
           BLUE_BAGE;
    }

コンパイルパス.説明C++のような付与方法はjavaには適用されません.だから、私が明らかにしなければならないのは:
1.javaプラットフォームで列挙値を初期化する方法.
2.上記のような列挙タイプColorSelectは、付与されていないのに、なぜswitch(ColorSelect)が実行できるのですか?列挙タイプは、文字列または整数値で一致します.
上記の問題を明らかにするために、私は自分で模範プログラムを書いて、そのプログラムをコンパイルした後、結果を見て、答えを見つけることができるかどうかを見ました.
一.列挙値の初期化方法
次は私の模範プログラムです.
自分で定義した列挙クラス:
public enum ColorSelect {   //     ColorSelect
           RED_BAGE ,   //        
           GREEN_BAGE,
           BLUE_BAGE;
    }

  逆コンパイルにより、このコードを逆コンパイルしたプログラムは次のとおりです.
public final class ColorSelect extends Enum
    {

        public static ColorSelect valueOf(String s)  //    ,             ColorSelect   
        {
            return (ColorSelect)Enum.valueOf(test/Enum/TestEnum$ColorSelect, s);
        }

        public static ColorSelect[] values()  //            ColorSelect  
        {
            ColorSelect acolorselect[] = ENUM$VALUES; //ENUM$VALUES ColorSelect  ,             
            int i = acolorselect.length;
            ColorSelect acolorselect1[] = new ColorSelect[i];
            System.arraycopy(acolorselect, 0, acolorselect1, 0, i);
            return acolorselect1;
        }

        public static final ColorSelect BLUE_BAGE; //       BLUE_BAGE,  ColorSelect  
        private static final ColorSelect ENUM$VALUES[]; //      ENUM$VALUES[],  ColorSelect  
        public static final ColorSelect GREEN_BAGE; //       GREEN_BAGE,  ColorSelect  
        public static final ColorSelect RED_BAGE;  //       RED_BAGE,  ColorSelect  
         static
		{//static      ,             ColorSelect       ,          ENUM$VALUES
            RED_BAGE = new ColorSelect("RED_BAGE", 0);  //   BLUE_BAGE
            GREEN_BAGE = new ColorSelect("GREEN_BAGE", 1);  //   GREEN_BAGE
            BLUE_BAGE = new ColorSelect("BLUE_BAGE", 2); //   BLUE_BAGE
            ColorSelect acolorselect[] = new ColorSelect[3];
            ColorSelect colorselect = RED_BAGE;
            acolorselect[0] = colorselect;
            ColorSelect colorselect1 = GREEN_BAGE;
            acolorselect[1] = colorselect1;
            ColorSelect colorselect2 = BLUE_BAGE;
            acolorselect[2] = colorselect2;
            ENUM$VALUES = acolorselect;
        }

        private ColorSelect(String s, int i) //    ,         ColorSelect  
        {
            super(s, i);
        }
    }
この逆コンパイルコードから見ると、2つの共通メンバー関数があります.
public static ColorSelect valueOf(String s);  この方法は、対応するColorSelectオブジェクトpublic static ColorSelect[]values()を文字列で取得する. このメソッドは、javaで直接呼び出すことができるすべてのColorSelectオブジェクトを取得します.
次はColorSelectのデータメンバーです. 
        public static final ColorSelect BLUE_BAGE;
        private static final ColorSelect ENUM$VALUES[];
        public static final ColorSelect GREEN_BAGE;
        public static final ColorSelect RED_BAGE;
公開データはBLUE_です.BAGE,GREEN_BAGE,RED_BAGE、それら自体の属性はColorSelectクラスです.したがって、ここから列挙クラス定義のメンバー変数もクラスであることがわかります.次のコード:
            RED_BAGE = new ColorSelect("RED_BAGE", 0);
            GREEN_BAGE = new ColorSelect("GREEN_BAGE", 1);
            BLUE_BAGE = new ColorSelect("BLUE_BAGE", 2);
の3行は、これら3つのクラスを初期化することに相当し、各クラスには一意のシーケンス番号が割り当てられ、定義された順序で0からインクリメントされる.JavaコードでColorSelect.BLUE_を呼び出すことができます.BAGE.ordinal()を使用して、対応するシーケンス番号値を取得します.
以上の分析によりjava列挙はクラスであり,言語自体ではなくコンパイラで実現され,中のメソッドを直接呼び出すことができる.Enum自体は普通のclassで、異なる機能を実現するために多くのカスタム方法があります.中のメソッドをカスタマイズしないと、コンパイラは初期化され、デフォルトの順序は0から増加します.メソッドをカスタマイズすることもできます.これにより、任意に値を割り当てることができます.
次は、カスタムjava列挙タイプで、値を割り当てることができます.
  public enum Temp {
        /*      ,                 ,      
         *              ,             
         *             ,       */
        absoluteZero(-459), freezing(32),boiling(212), paperBurns(451);
        
        private final int value;
        public int getValue() {
            return value;
        }
        //         private,                
        Temp(int value) {
            this.value = value;
        }
    }

これは、値を割り当てるためにカスタムメソッドを定義する列挙クラスです.JAvaはC++のように簡単ではなく、最初の値で、その後の値が増加します.割り当ては、すべて割り当てられているか、または割り当てられていない必要があります.一部は割り当てられていません.一部は割り当てられていません.値を割り当てないとコンストラクタは書けず、値のコンパイルもエラーです.
  Temp temp = null;
        Log.i("Temp##",temp.freezing.getValue()+""); 
メイン関数ではtemp.freezing.getValue()を呼び出し、対応する値32を得る.
カスタム列挙クラスが面倒だと感じたら、カスタム列挙クラスの代わりに別の方法で使用することもできます.次のようになります.
	public class ColorSelect {
         private static final int  RED_BAGE = 1;
         private static final int  GREEN_BAGE = 3;
         private static final int  BLUE_BAGE = 5;
    }

この方法も便利です.
二.最初の疑問が明らかになった.私たちは今2番目の問題を解決しなければならない.Switchは文字列か整数かで列挙変数を分解する.
次はjavaコードセグメントです.
   ColorSelect test = ColorSelect.BLUE_BAGE;
  
        switch(test){
        case RED_BAGE:
        	Log.i("TEST####1","a");
        	break;
        case GREEN_BAGE:
        	Log.i("TEST####2","b");
        	break;
        case BLUE_BAGE:
        	Log.i("TEST####3","c");
        	break;
        	default:
            Log.i("TEST####4","d");
        }
   
       Log.i("TEST####ret", "e");

逆コンパイルされたコード:
{  //   
 ColorSelect colorselect = ColorSelect.BLUE_BAGE;
        ai = $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect();  //     $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect(),       ai
        i = colorselect.ordinal();  //       BLUE_BAGE   
        ai[i]; //switch  ai[i]      
        JVM INSTR tableswitch 1 3: default 56 // 1 3 ai[i]     
    //                   1 73
    //                   2 84
    //                   3 95;
           goto _L1 _L2 _L3 _L4
_L1:
        Log.i("TEST####4", "d");
_L6:
        Log.i("TEST####ret", "e");
        return;
_L2:
        Log.i("TEST####1", "a");
        continue; /* Loop/switch isn't completed */
_L3:
        Log.i("TEST####2", "b");
        continue; /* Loop/switch isn't completed */
_L4:
        Log.i("TEST####3", "c");
        if(true) goto _L6; else goto _L5
_L5:
    }

    private static int $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect[];
}

//   
 static int[] $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect()  //
    {
        int ai[] = $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect;
        if(ai == null)
        {
            ai = new int[ColorSelect.values().length];  
            try  //     ,                ai   ,        
            {
                int i = ColorSelect.BLUE_BAGE.ordinal();  
                ai[i] = 3;
            }
            catch(NoSuchFieldError nosuchfielderror2) { }
            try
            {
                int j = ColorSelect.GREEN_BAGE.ordinal();
                ai[j] = 2;
            }
            catch(NoSuchFieldError nosuchfielderror1) { }
            try
            {
                int k = ColorSelect.RED_BAGE.ordinal();
                ai[k] = 1;
            }
            catch(NoSuchFieldError nosuchfielderror) { }
            $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect = ai;
        }
        return ai;
    }

サブ関数$SWITCH_TABLE$test$Enum$TestEnum$ColorSelect()の機能:
int i=ColorSelect.BLUE_でBAGE.ordinal()は、クラスの一意のシーケンス番号を配列の下付きとして取得し、ai[i]に付与する.このように、各列挙クラス対応には一意の整数対応がある.次に配列ai[]を返します.この機能はコンパイラによって実現されます.サブ関数の機能は余計な感じがしますが、実際には前に列挙クラスを初期化したときの下の記号を直接使って、識別子としてもいいし、列挙クラスごとに数値を再割り当てする必要はありません.
メイン関数で、サブ関数$SWITCH_を呼び出します.TABLE$test$Enum$TestEnum$ColorSelect()、switch()内のパラメータはai[i]であり、この整数値から分岐を判断する.Java内の列挙値はコンパイラによって各列挙タイプに整数値を付与し,Switchはこれらの整数を識別することによって分岐を判断し,2つ目の疑問を解決した.同時にswitchのjavaコードと逆コンパイルのコードを比較することで,おおよそ次の結論を得ることができる.1.各フラグビットの「continue」は、ブランチbreakを示す.2.最後のブランチのフラグはif(true)goto_Lx,else goto _x.
同時にswitchのjavaコードと逆コンパイルのコードを比較することで,おおよそ次の結論を得ることができる.1.各フラグビットの「continue」は、ブランチbreakを示す.2.最後のブランチのフラグはif(true)goto_Lx,else goto _x.
同時に、まだ疑問があって、理解していませんか?
私はいくつかのswitchの例を書いて、逆コンパイルしたコードには共通の法則があります.
逆コンパイルコード1:
  JVM INSTR tableswitch 1 3: default 56
    //                   1 73
    //                   2 84
    //                   3 95;
           goto _L1 _L2 _L3 _L4

逆コンパイルコード2:
JVM INSTR tableswitch 0 2: default 60
    //                   0 77
    //                   1 88
    //                   2 99;
           goto _L1 _L2 _L3 _L4

この2つは,1,2,3と表記され,互いに値が11ずつ異なる.二つ目も.これは分からないの?また、これらの数値は何を表していますか?
以上、何か間違っているところがあれば、ご指摘ください.