理解できないJava列挙型

列挙もJavaの古い話です。列挙する必要があるデータのセットに遭遇するときはいつでも、当然、列挙型を使用します。

public enum Color { RED、GREEN、BLUE、YELLOW;

public static void main(String[] args) {
    Color red = Color.RED;
    Color redAnother = Color.RED;
    Color blue = Color.BLUE;

    System.out.println(red.equals(redAnother)); // true
    System.out.println(red.equals(blue)); // false
}

}
もちろん、私たちが今日議論しようとしていることはJavaで列挙型の基本的な構文ではありません。このトピックでは、列挙型の本質に入ると、いくつかの高レベルの使用法を説明します。この記事は、Oracle JDK 14.0.2およびjad v1.5.8eに基づいています(jadは長期間更新されていないため、新しいバージョンのjdkのサポートは完全ではありませんが、列挙型とインターフェースの分析で十分です)。

カスタム列挙値の背後にある秘密列挙の
デフォルト値は0から増加する値で、通常はこれで十分です。ただし、Javaでは、列挙値をパーソナライズすることもできます。次に例を示します。

//オリエンテーションを英語で使用するだけでなく、対応するローカライズされた名前(ここでは中国語)を取得したい
enum Direction { EAST(“东”)、WEST(“西”)、NORTH(“北”)、SOUTH( "南");



private final String name;

// 注意是private
private Direction(String name) {
    this.name = name;
}

public String getName() {
    return this.name;
}

}

public class Test { public static void main(String [] args){ for(var v:Direction.values()){ System.out.println(v.toString()+“ –>” + v.getName()) ; } } } プログラムをコンパイルして実行すると、次の結果が得られます。






EAST–> East
WEST–> West
NORTH–> North
SOUTH–> South
これで、多くのチュートリアルは終了です。これまでのところ、列挙値の後の括弧の効果は何ですか?コンストラクタにプライベートな変更が必要なのはなぜですか?説明はありません。ただし、これらを理解することは、列挙型の高度な使用法をさらに研究するための前提です。

しかし、それは問題ではありません。たとえば、逆コンパイルされたコードを調べて、コンパイラが列挙型を処理する方法を調べるなど、それを自分で調べることができます。ここでは、jadを使用し、公園内の他の優れた記事で特定の使用法のチュートリアルを参照します。この記事では詳細には触れませんが、逆コンパイル後の結果を直接確認します。

最終クラスDirectionはEnum
{

/* 省略部分无关紧要的方法 */

private Direction(String s, int i, String s1)
{
    super(s, i);
    name = s1;
}

public String getName() // 这是我们自定义的getter
{
    return name;
}

public static final Direction EAST;
public static final Direction WEST;
public static final Direction NORTH;
public static final Direction SOUTH;
private final String name;
// 省略不重要的部分字段

static 
{
    EAST = new Direction("EAST", 0, "\u4E1C");
    WEST = new Direction("WEST", 1, "\u897F");
    NORTH = new Direction("NORTH", 2, "\u5317");
    SOUTH = new Direction("SOUTH", 3, "\u5357");
    // 省略部分字段的初始化
}

}
まず第一に、私たちは私たちの列挙型がクラスであることがわかり、そして第二に、それはjava.lang.Enum(列挙型が明示的に基本クラスを指定することができないことを意味する)から継承し、我々は方向のコンストラクタで親クラスの構造を呼び出します関数、ドキュメントを読むと、java.lang.Enumのコンストラクターが保護されていることがわかります。つまり、java.langパッケージ外のユーザーはこのコンストラクターを呼び出すことができません。同時に、ドキュメントはコンストラクタがコンパイラによって自動的に呼び出されることも指摘しました。したがって、自分で定義した列挙型コンストラクターは正常に呼び出すことができず、コンパイラーは列挙型メンバーを初期化するためにのみ使用できます。ユーザーが呼び出すことはできないため、Javaは、保護されたパブリック(デフォルトおよびプライベートの許可)がカスタム列挙型のコンストラクターを変更して、誤用を回避することを単に許可しません。

さらに、カスタムコンストラクターは実際にはコンパイラーによって合成されます。カスタムパラメーターに加えて、列挙型メンバーの文字列名と0から始まるシリアル番号があります(序数メソッドで取得できます)。最初の2つのパラメーターはコンパイルされますデバイスは自動的に追加され、カスタムパラメータは、列挙したメンバーの後の括弧内の値に基づいてコンストラクタに渡されます。

EAST( "東")、
WEST( "西")、
NORTH( "北")、
SOUTH( "南");

//(Unicode文字はトランスコードされます)
EAST = new Direction(“ EAST”、0、“ \ u4E1C”);
WEST = new Direction(“ WEST”、1、“ \ u897F”);
NORTH = new Direction( "NORTH"、2、 "\ u5317");
SOUTH = new Direction( "SOUTH"、3、 "\ u5357");
さらにフィールドが必要な場合は、次のようにします。

public enum Planet { // 2つのカスタム値MERCURY(3.303e + 23、2.4397e6)、VENUS(4.869e + 24、6.0518e6)、EARTH(5.976e + 24、6.37814e6)、MARS(6.421e + 23、3.3972e6)、JUPITER(1.9e + 27、7.1492e7)、SATURN(5.688e + 26、6.0268e7)、天王星(8.686e + 25、2.5559e7)、NEPTUNE(1.024e + 26、2.4746e7) ;








// 保存自定义值的字段,不使用final也可以,但枚举值一般不应该发生改变
private final double mass;   // in kilograms
private final double radius; // in meters
// 在这里使用default的权限控制,即package-private
Planet(double mass, double radius) {
    this.mass = mass;
    this.radius = radius;
}
public double mass() { return mass; }
public double radius() { return radius; }

}
これは、カスタム列挙値の背後にある秘密です。

これまでのところ、私たちの質問はほぼ答えられていますが、注意深く観察すると、列挙型メンバーがすべてDirectionの_staticフィールド_であることがわかります。したがって、これらの列挙型メンバーを型として使用することはできません。

public void work(Direction.EAST e){ //これはコンパイルできません} 静的フィールドはよく理解されています。クラス名+列挙型メンバー名Direction.WESTを使用して直接参照する必要があるためですが、フィールドタイプはDirectionである必要があるのはなぜですか。 ?


心配しないでください、答えは次のセクションで明らかにされます。

列挙への抽象メソッドの追加
このセクションはばかげているようです。抽象メソッドは列挙と互換性がないようです。しかし、慎重に検討してください。前のセクションでは、列挙型にゲッターメンバーメソッドを追加しました。これは、列挙型に他のメソッドを追加して、列挙型の動作をカスタマイズできることを示しています。前のセクションの惑星は例です。惑星の表面にあるオブジェクトの重力と質量の計算を追加できます。

public enum Planet {

/* 定义枚举成员和初始化的相关重复代码,此处不再重复 */

private double mass() { return mass; }
private double radius() { return radius; }

// universal gravitational constant  (m3 kg-1 s-2)
public static final double G = 6.67300E-11;

double surfaceGravity() {
    return G * mass / (radius * radius);
}
double surfaceWeight(double otherMass) {
    return otherMass * surfaceGravity();
}
public static void main(String[] args) {
    if (args.length != 1) {
        System.err.println("Usage: java Planet <earth_weight>");
        System.exit(-1);
    }
    double earthWeight = Double.parseDouble(args[0]);
    double mass = earthWeight/EARTH.surfaceGravity();
    for (Planet p : Planet.values())
       System.out.printf("Your weight on %s is %f%n",
                         p, p.surfaceWeight(mass));
}

}
実行結果は次のとおりです。

$ java Planet.java 70

MERCURY ONあなたの体重は26.443033 IS
VENUSであなたの体重63.349937 IS
地球上のあなたの体重70.000000 IS
MARSであなたの体重26.511603 IS
JUPITERであなたの体重177.139027 IS
SATURNであなたの体重74.621088 IS
63.358904 IS URANUSであなたの体重
NEPTUNEであなたの体重79.682965 IS
彼らことを考えます列挙型全体の動作をカスタマイズするということは、列挙型メンバーの動作を個別に定義できるということです。結局、メソッドは列挙型メンバーの値から最終的に呼び出されます。

前のセクションの最後の部分でコンパイラが列挙型メンバーをどのように処理したか覚えていますか?

EAST = new Direction(“ EAST”、0、“ \ u4E1C”);
WEST = new Direction(“ WEST”、1、“ \ u897F”);
NORTH = new Direction(“ NORTH”、2、“ \ u5317”) ;
SOUTH = new Direction( "SOUTH"、3、 "\ u5357");
はい、列挙型メンバー自体も列挙型オブジェクトのインスタンスです!これらの列挙型メンバーはDirection型ですが、実際には派生型のDirectionを参照できます。

Colorタイプの列挙があり、列挙メンバーごとに異なる情報を印刷するためのカスタム印刷メソッドがあるとします。

enum Color { RED { //ここでは構文について心配しないで、後で説明します@Override public void print(){ // Linuxでカラー文字列を出力しますSystem.out.println( "\ u001B [1; 31mこれは赤ですtext \ u001B [0m "); } }、BLUE { @Override public void print(){ System.out.println(" \ u001B [1; 34m This is blue text \ u001B [0m "); } }、GREEN { @Override public void print(){ System.out.println( "\ u001B [1; 32mこれは緑色のテキストです\ u001B [0m"); } };



















// 枚举成员必须要覆写的抽象方法
public abstract void print();

}

public class Test { public static void main(String [] args){ for(var v:Color.values()){ v.print(); } } } 実行結果は次のとおりです。






原則を知るために、Color.classの処理済みコンテンツであるjadを使用する必要があります。

//抽象クラスになります
抽象クラスColor extends Enum
{ //コンストラクタprivate color(String s、int i){ super(s、i); }




public abstract void print();

public static final Color RED;
public static final Color BLUE;
public static final Color GREEN;

static 
{
    // 重点从这开始
    RED = new Color("RED", 0) {

        public void print()
        {
            System.out.println("\033[1;31m This is red text \033[0m");
        }

    };
    BLUE = new Color("BLUE", 1) {

        public void print()
        {
            System.out.println("\033[1;34m This is blue text \033[0m");
        }

    };
    GREEN = new Color("GREEN", 2) {

        public void print()
        {
            System.out.println("\033[1;32m This is green text \033[0m");
        }

    };
}

}
注意深い読者はおそらく知っているでしょう、これは_anonymous inner class_ではありませんか?そうです、今回の列挙型は実際には抽象クラスになり、列挙型メンバーはColorから継承して抽象メソッドを実装する匿名の内部クラスです。したがって、最初にコメントでマークした中括弧は、実際には匿名クラスのクラス本体として理解できます。ただし、ここでは明示的に匿名の内部クラスを作成するためにnewが使用されていますが、コンストラクターは引き続きコンパイラーによって呼び出されます。

カスタム列挙データを追加したい場合はどうなりますか?あなたはこれを行うことができます:

列挙カラー{ RED(31){ @Override 公共ボイドプリント(){ System.out.printlnは( "\ u001B [1; 31メートルこれは、赤色のテキスト\ u001B [0メートルです")。} }、BLUE(34){ @Override 公共ボイドプリント(){ System.out.printlnは( "\ u001B [1; 34メートルこれは青色のテキスト\ u001B [0メートル")。} }、GREEN(32){ @Override 公共ボイドプリント(){ System.out.printlnは( "\ u001B [1; 32メートルこれは緑色のテキスト\ u001B [0メートル")。} };

















// color code
private final int colorCode;
private Color(int code) {
    colorCode = code;
}
public int getColorCode() {
    return colorCode;
}

public abstract void print();

}
コンパイルされたコードを見てみましょう。スペースの制限のため、重要な部分のみを残しました。

抽象クラスColorはEnum
{

/* 大量省略代码 */

private Color(String s, int i, int j)
{
    super(s, i);
    colorCode = j;
}

public abstract void print();

public static final Color RED;
public static final Color BLUE;
public static final Color GREEN;
private final int colorCode;

static 
{
    // 参数传递给了构造函数
    RED = new Color("RED", 0, 31) {

        public void print()
        {
            System.out.println("\033[1;31m This is red text \033[0m");
        }

    };
    BLUE = new Color("BLUE", 1, 34) {

        public void print()
        {
            System.out.println("\033[1;34m This is blue text \033[0m");
        }

    };
    GREEN = new Color("GREEN", 2, 32) {

        public void print()
        {
            System.out.println("\033[1;32m This is green text \033[0m");
        }

    };
}

}
列挙型のため、要約すると、以下の形式が通常存在します。

[public] enum NAME [implements XXX、…] { VALUE1 [(カスタムデータ、形式はカスタムコンストラクター関数のパラメーターリストと同じ)] [{ //新しいメソッドをオーバーライドまたは追加できます}]、…、VALUEN [(…)] [{ //オーバーライドまたはメソッド}];








[存储各种自定义数据的字段,最好用final修饰]
[
    // 自定义构造函数
    [private] NAME(和枚举成员中给出的圆括号内的内容一致) { /* 设置数据字段 */ }
]

[定义抽象方法或者重写object/Enum的方法或是添加普通类方法]

}
[]で囲まれた部分は省略可能です。

列挙とインターフェース
前のセクションの最後で、列挙型が実際にインターフェースを実装できることを確認しました(結局、それは本質的にクラスです)。したがって、前のセクションの例は次のように書くことができます。

インターフェースPrinter { void print(); }

enum ColorはPrinter { RED { @Override public void print(){ System.out.println( "\ u001B [1; 31m This is red text \ u001B [0m"); } }、BLUE { @Override public void print( ){ System.out.println( "\ u001B [1; 34m This is blue text \ u001B [0m"); } }、GREEN { @Override public void print(){ System.out.println( "\ u001B [1 ; 32mこれは緑色のテキストです\ u001B [0m "); } }; } enumは主にデータのコレクションであり、データによって示される動作/パターンはできるだけインターフェイスを使用して記述されるため、私は個人的に2番目の方法を好みます。



















さらに、列挙型はiinterfaceで定義することもできます。月曜日から日曜日までを表す列挙型があり、メソッドisRestDayを指定して、現在の日付を休息できるかどうかを判断するとします(たとえば、一部の人々は週末を持ち、一部の人々は1日の休日を持ち、一部の人々は月曜日または金曜日に休息します)。曜日によって回答の種類が異なるため、インターフェースとして抽象化する方が適切です。

インターフェースRelaxable { enum Weekly { Mon、Tue、Wed、Thu、Fri、Sat、Sun }


boolean isRestDay(Relaxable.Weekly day);

}

クラスPersonAはRelaxable { @Override public boolean isRestDay(Relaxable.Weekly day){ return day.equals(Relaxable.Weekly.Sat)||を実装します day.equals(Relaxable.Weekly.Sun); } }




クラスPersonBはRelaxable { @Override public boolean isRestDay(Relaxable.Weekly day){ return day.equals(Relaxable.Weekly.Sun);を実装します。} }




public class Relax { public static void main(String [] args){ var a = new PersonA(); var b = new PersonB(); var day = Relaxable.Weekly.Sat ; System.out.println(a.isRestDay( day)); // true System.out.println(b.isRestDay(day)); // false } } PersonAは素晴らしい週末を過ごし、貧しいPersonBは土曜日に残業しなければなりません!jadを使用して、生成されたコードを表示します。








インターフェースRelaxable
{ public static final class Weekly extends Enum {

    /* 省略了部分代码 */

    public static final Weekly Mon;
    public static final Weekly Tue;
    public static final Weekly Wed;
    public static final Weekly Thu;
    public static final Weekly Fri;
    public static final Weekly Sat;
    public static final Weekly Sun;

    static 
    {
        Mon = new Weekly("Mon", 0);
        Tue = new Weekly("Tue", 1);
        Wed = new Weekly("Wed", 2);
        Thu = new Weekly("Thu", 3);
        Fri = new Weekly("Fri", 4);
        Sat = new Weekly("Sat", 5);
        Sun = new Weekly("Sun", 6);
    }

    private Weekly(String s, int i)
    {
        super(s, i);
    }
}

public abstract boolean isRestDay(Weekly weekly);

}
アマゾン評価www.yisuping.com

おすすめ

転載: blog.csdn.net/weixin_45032957/article/details/108511126