Metode Java Enum - mengembalikan enum arah berlawanan


113

Saya ingin mendeklarasikan Arah enum, yang memiliki metode yang mengembalikan arah berlawanan (berikut ini tidak benar secara sintaksis, yaitu, enum tidak dapat dipakai, tetapi itu menggambarkan maksud saya). Apakah ini mungkin di Jawa?

Ini kodenya:

public enum Direction {

     NORTH(1),
     SOUTH(-1),
     EAST(-2),
     WEST(2);

     Direction(int code){
          this.code=code;
     }
     protected int code;
     public int getCode() {
           return this.code;
     }
     static Direction getOppositeDirection(Direction d){
           return new Direction(d.getCode() * -1);
     }
}

Cukup gunakan sakelar. Anda hanya memiliki 4 kasus.
Sotirios Delimanolis

12
Ngomong-ngomong, d.getCode() * -1==-d.getCode()
tckmn

Bab 6 (setidaknya dalam 2E) dari Bloch's Effective Java mungkin menarik, dan sangat direkomendasikan.
jedwards

ntu.edu.sg/home/ehchua/programming/java/javaenum.html , bagian 2.1 telah menjelaskan konsep tersebut dengan rapi
vikramvi

Jawaban:


206

Bagi mereka yang terpikat di sini dengan judul: ya, Anda dapat menentukan metode Anda sendiri di enum Anda. Jika Anda bertanya-tanya bagaimana cara memanggil metode non-statis tersebut, Anda melakukannya dengan cara yang sama seperti dengan metode non-statis lainnya - Anda memanggilnya pada contoh tipe yang mendefinisikan atau mewarisi metode itu. Dalam kasus enum contoh seperti itu hanya ENUM_CONSTANTs.

Jadi yang Anda butuhkan hanyalah EnumType.ENUM_CONSTANT.methodName(arguments).


Sekarang mari kita kembali ke masalah dari pertanyaan. Salah satu solusinya bisa jadi

public enum Direction {

    NORTH, SOUTH, EAST, WEST;

    private Direction opposite;

    static {
        NORTH.opposite = SOUTH;
        SOUTH.opposite = NORTH;
        EAST.opposite = WEST;
        WEST.opposite = EAST;
    }

    public Direction getOppositeDirection() {
        return opposite;
    }

}

Sekarang Direction.NORTH.getOppositeDirection()akan kembali Direction.SOUTH.


Berikut adalah cara yang lebih "hacky" untuk mengilustrasikan komentar @jedwards tetapi tidak terasa sefleksibel pendekatan pertama karena menambahkan lebih banyak bidang atau mengubah urutannya akan merusak kode kita.

public enum Direction {
    NORTH, EAST, SOUTH, WEST;

    // cached values to avoid recreating such array each time method is called
    private static final Direction[] VALUES = values();

    public Direction getOppositeDirection() {
        return VALUES[(ordinal() + 2) % 4]; 
    }
}

3
Saya akan melakukan .values()[ordinal()]peretasan tetapi pendekatan ini jauh lebih kuat
mulai

bagaimana Anda menggunakannya? dan apa sebutan teknik ini?
Thufir

1
@Thufir " bagaimana Anda menggunakannya " jika Anda bertanya tentang metode, lalu seperti metode lainnya - Anda memanggilnya pada instance kelas dengan metode ini. Contoh Directionkelas enum adalah NORTH, EAST, SOUTH, WESTsehingga Anda hanya dapat menggunakan NORTH.getOppositeDirection()dan akan kembali SOUTH. " Apa nama teknik ini? " jika Anda bertanya tentang static{...}itu maka itu adalah blok inisialisasi statis , itu adalah kode yang dipanggil ketika kelas dimuat pertama kali (ini adalah bagian dari proses yang sama yang menginisialisasi bidang statis).
Pshemo

@Pshemo, saya bertanya-tanya bagaimana versi Spring dari kode di atas, jika nilai yang ditetapkan di blok statis perlu disuntikkan dari file properti katakan.
Vikas Prasad

161

Untuk enum kecil seperti ini, saya menemukan solusi yang paling mudah dibaca:

public enum Direction {

    NORTH {
        @Override
        public Direction getOppositeDirection() {
            return SOUTH;
        }
    }, 
    SOUTH {
        @Override
        public Direction getOppositeDirection() {
            return NORTH;
        }
    },
    EAST {
        @Override
        public Direction getOppositeDirection() {
            return WEST;
        }
    },
    WEST {
        @Override
        public Direction getOppositeDirection() {
            return EAST;
        }
    };


    public abstract Direction getOppositeDirection();

}

8
Ide yang hebat! Ini juga bagus ketika Anda biasanya ingin setiap nilai enum memiliki perilaku tertentu.
OferBr

28

Ini bekerja:

public enum Direction {
    NORTH, SOUTH, EAST, WEST;

    public Direction oppose() {
        switch(this) {
            case NORTH: return SOUTH;
            case SOUTH: return NORTH;
            case EAST:  return WEST;
            case WEST:  return EAST;
        }
        throw new RuntimeException("Case not implemented");
    }
}

8
Daripada mengembalikan null, klausa default yang menampilkan RuntimeException yang sesuai mungkin lebih baik untuk menunjukkan bahwa ada kesalahan programmer karena tidak mendefinisikan kebalikan dari arah yang baru ditambahkan.
Timothy055

1
Ini membutuhkan pemanggil untuk menangani null. Ini juga membutuhkan pengelola untuk memastikan mereka menambahkan kasus setiap kali jenis Arah baru ditambahkan. Lihat jawaban Amir Afghani tentang penggunaan metode abstrak yang dapat diganti oleh setiap nilai enum, dengan begitu Anda tidak akan pernah mengambil risiko kehilangan satu pun, dan Anda tidak perlu khawatir menangani null.
Michael Peterson

14

Buat metode abstrak, dan minta setiap nilai enumerasi Anda menimpanya. Karena Anda mengetahui kebalikannya saat Anda membuatnya, tidak perlu membuat atau membuatnya secara dinamis.

Itu tidak terbaca dengan baik; mungkin switchakan lebih mudah dikelola?

public enum Direction {
    NORTH(1) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.SOUTH;
        }
    },
    SOUTH(-1) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.NORTH;
        }
    },
    EAST(-2) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.WEST;
        }
    },
    WEST(2) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.EAST;
        }
    };

    Direction(int code){
        this.code=code;
    }
    protected int code;

    public int getCode() {
        return this.code;
    }

    public abstract Direction getOppositeDirection();
}

4

Ya, kami melakukannya sepanjang waktu. Anda mengembalikan instance statis daripada Objek baru

 static Direction getOppositeDirection(Direction d){
       Direction result = null;
       if (d != null){
           int newCode = -d.getCode();
           for (Direction direction : Direction.values()){
               if (d.getCode() == newCode){
                   result = direction;
               }
           }
       }
       return result;
 }

0
public enum Direction {
    NORTH, EAST, SOUTH, WEST;

    public Direction getOppositeDirection(){
        return Direction.values()[(this.ordinal() + 2) % 4];
    }
}

Enum memiliki metode nilai statis yang mengembalikan larik yang berisi semua nilai enum sesuai urutan pernyataannya. sumber

karena UTARA mendapat 1, TIMUR mendapat 2, SELATAN mendapat 3, BARAT mendapat 4; Anda dapat membuat persamaan sederhana untuk mendapatkan persamaan sebaliknya:

(nilai + 2)% 4


2
kenapa ini jawabannya? Bagaimana Anda mengharapkan ini untuk membantu pembaca di masa depan, atau siapa pun dalam hal ini, belajar tanpa penjelasan apa pun?
GrumpyCrouton

Meskipun kode ini dapat menjawab pertanyaan, memberikan konteks tambahan tentang bagaimana dan / atau mengapa kode ini memecahkan masalah akan meningkatkan nilai jangka panjang jawaban. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, bukan hanya orang yang bertanya sekarang! Harap edit jawaban Anda untuk menambahkan penjelasan, dan berikan indikasi batasan dan asumsi apa yang berlaku. Tidak ada salahnya juga untuk menyebutkan mengapa jawaban ini lebih tepat daripada yang lain.
ItamarG3

apakah sulit membaca kode tanpa komentar? atau apakah Anda memerlukan javadoc eksklusif untuk 7 baris kode?
Pregunton

1
Solusi ini rapuh karena bergantung pada urutan nilai enum. Jika Anda mengubah urutan menjadi abjad, persamaan pintar Anda tidak akan lagi memberikan kebalikan yang benar.
Josh J
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.