Cast Int to enum di Jawa

335

Apa cara yang benar untuk melemparkan Int ke enum di Jawa mengingat enum berikut?

public enum MyEnum
{
    EnumValue1,
    EnumValue2
}


MyEnum enumValue = (MyEnum) x; //Doesn't work???
Maxim Gershkovich
sumber
@RyuS. Itu bukan duplikat dari pertanyaan ini,
Mark Rotteveel

Jawaban:

588

Coba di MyEnum.values()[x]mana xharus 0atau 1, yaitu ordinal yang valid untuk enum itu.

Perhatikan bahwa dalam Java enum sebenarnya adalah kelas (dan nilai enum dengan demikian adalah objek) dan dengan demikian Anda tidak dapat melemparkan intatau bahkan Integerke enum.

Thomas
sumber
117
+1: Anda mungkin ingin melakukan cache MyEnum.values()karena harganya mahal. yaitu jika Anda menyebutnya ratusan kali.
Peter Lawrey
6
@PeterLawrey Hanya untuk kelengkapan, bisakah Anda menjelaskan mengapa itu harus lambat? Saya tidak melihat alasan yang jelas untuk itu.
Tarrasch
48
@ Tarrasch karena array dapat diubah, nilai () harus mengembalikan salinan array elemen kalau-kalau Anda mengubahnya. Membuat salinan ini setiap kali relatif mahal.
Peter Lawrey
9
@PeterLawrey Saya telah menggunakan banyak haskell akhir-akhir ini (di mana semuanya tidak berubah)! Terima kasih atas penjelasan Anda yang jelas dan ringkas. :)
Tarrasch
bagaimana cara mendapatkan 'x'?
Beeing Jk
161

MyEnum.values()[x]adalah operasi yang mahal. Jika kinerjanya mengkhawatirkan, Anda mungkin ingin melakukan sesuatu seperti ini:

public enum MyEnum {
    EnumValue1,
    EnumValue2;

    public static MyEnum fromInteger(int x) {
        switch(x) {
        case 0:
            return EnumValue1;
        case 1:
            return EnumValue2;
        }
        return null;
    }
}
Lorenzo Polidori
sumber
44
Jika Anda ingin menghindari pemeliharaan sakelar, maka pada kelas using: private final MyEnum [] myEnumValues ​​= MyEnum.values ​​(); Kemudian penggunaan: myEnum = myEnumValues ​​[i];
Gili Nachum
23
@GiliNachum mengatakannya dengan cara yang aneh, tetapi masalah dengan solusi ini adalah rawatan. Ini bertentangan dengan prinsip KERING, yang berarti setiap kali nilai enum diubah (disusun ulang, nilai ditambahkan, nilai dihapus) pernyataan switch harus diperbarui secara bersamaan. Komentar Gili memaksa Jawa untuk mempertahankan konsistensi dengan daftar nilai enum, perubahan pada nilai enum tidak mempengaruhi pendekatan itu sama sekali.
Dandre Allison
3
@LorenzoPolidori, dapatkah Anda menjelaskan mengapa Anda menganggapnya MyEnum.values()[x]sebagai operasi yang mahal. Saya tidak tahu cara kerjanya secara detail, tetapi bagi saya sepertinya mengakses elemen dalam Array bukan masalah besar, alias waktu yang konstan. Jika array harus dibangun, dibutuhkan O (n) waktu, yang merupakan waktu runnning yang sama dengan solusi Anda.
brunsgaard
1
@ brunsgaard Saya berasumsi values()menghasilkan array baru setiap kali, karena array bisa berubah sehingga tidak aman untuk mengembalikan yang sama beberapa kali. Beralih pernyataan tidak harus O (n), mereka dapat dikompilasi ke tabel lompatan. Jadi klaim Lorenzo tampaknya bisa dibenarkan.
MikeFHay
1
@Johnson Gili versi tidak memerlukan perubahan kode tambahan di luar perubahan dalam deklarasi Enum. Jika Anda menambahkan item baru ke enum, myEnum = myEnumValues[i]masih akan mengembalikan iitem th di Enum tanpa perubahan.
Dandre Allison
45

Jika Anda ingin memberikan nilai integer Anda, Anda dapat menggunakan struktur seperti di bawah ini

public enum A
{
        B(0),
        C(10),
        None(11);
        int id;
        private A(int i){id = i;}

        public int GetID(){return id;}
        public boolean IsEmpty(){return this.equals(A.None);}
        public boolean Compare(int i){return id == i;}
        public static A GetValue(int _id)
        {
            A[] As = A.values();
            for(int i = 0; i < As.length; i++)
            {
                if(As[i].Compare(_id))
                    return As[i];
            }
            return A.None;
        }
}
Dokter
sumber
5
+1 karena menyoroti fakta, bahwa nilai tidak harus berturut-turut.
rhavin
Selain itu, ini tampaknya menjadi satu-satunya jawaban yang berfungsi jika Anda menginginkan sekumpulan nilai integer yang jarang (atau berulang) daripada menggunakan ordinals default dari 0 .. (hitung-1). Itu bisa menjadi penting jika Anda berinteraksi dengan kode yang ada, seperti melalui jaringan.
benkc
Layak menunjukkan bahwa seperti dalam jawaban di atas, caching hasil nilai () mungkin bermanfaat, sehingga untuk menghindari alokasi memori dan arraycopy setiap kali Anda memintanya.
benkc
1
Dan, tergantung pada panjang enum Anda, Anda mungkin ingin membuat HashMap atau menggunakan pencarian biner atau sesuatu untuk pencarian ini, daripada melakukan pencarian linear setiap waktu.
benkc
2
Ini harus menjadi cara yang benar dan praktik terbaik untuk mengonversi int menjadi enum, dan saya pikir Anda dapat menyederhanakan masalah dengan public static A GetValue (int _id) {for (A: A.values ​​() {if (a.getId ( ) == _ id) {return a;}} return null;} Singkirkan barang-barang None, isEmpty () dan bandingkan ()
Chris.Zou
35

Anda bisa coba seperti ini.
Buat Kelas dengan id elemen.

      public Enum MyEnum {
        THIS(5),
        THAT(16),
        THE_OTHER(35);

        private int id; // Could be other data type besides int
        private MyEnum(int id) {
            this.id = id;
        }

        public static MyEnum fromId(int id) {
                for (MyEnum type : values()) {
                    if (type.getId() == id) {
                        return type;
                    }
                }
                return null;
            }
      }

Sekarang Ambil Enum ini menggunakan id sebagai int.

MyEnum myEnum = MyEnum.fromId(5);
Piyush Ghediya
sumber
19

Saya menyimpan nilai-nilai dan membuat metode akses statis sederhana:

public static enum EnumAttributeType {
    ENUM_1,
    ENUM_2;
    private static EnumAttributeType[] values = null;
    public static EnumAttributeType fromInt(int i) {
        if(EnumAttributeType.values == null) {
            EnumAttributeType.values = EnumAttributeType.values();
        }
        return EnumAttributeType.values[i];
    }
}
ossys
sumber
4
Ini adalah solusi yang saya gunakan sekarang. Tapi IMHO itu kurang membingungkan jika Anda tidak memberikan bidang valuesnama yang sama dengan metode values(). Saya menggunakan cachedValuesuntuk nama bidang.
ToolmakerSteve
2
Efisien dan elegan; disalin dan disisipkan ke proyek saya :) Satu-satunya hal yang saya ubah adalah fromInt (int i), yang baru saja saya panggil dari (int i) karena agak berlebihan untuk memiliki int dua kali dalam tanda tangan.
pipedreambomb
1
mengapa tidak menginisialisasi dari awal? mengapa menunggu hit pertama?
kaiser
9

Java enum tidak memiliki pemetaan enum-ke-int yang sama dengan yang mereka lakukan di C ++.

Yang mengatakan, semua enum memiliki valuesmetode yang mengembalikan array nilai enum yang mungkin, jadi

MyEnum enumValue = MyEnum.values()[x];

harus bekerja. Agak jahat dan mungkin lebih baik untuk tidak mencoba dan mengkonversi dari ints ke Enums (atau sebaliknya) jika memungkinkan.

Cameron Skinner
sumber
9

Ini bukan sesuatu yang biasanya dilakukan, jadi saya akan mempertimbangkan kembali. Tetapi setelah mengatakan itu, operasi mendasar adalah: int -> enum menggunakan EnumType.values ​​() [intNum], dan enum -> int menggunakan enumInst.ordinal ().

Namun, karena implementasi nilai apa pun () tidak punya pilihan selain memberi Anda salinan array (array java tidak pernah hanya baca), Anda akan lebih baik dilayani menggunakan EnumMap untuk cache pemetaan enum -> int.

Dilum Ranatunga
sumber
2
Re "Ini bukan sesuatu yang biasanya dilakukan": Kasus umum di mana berguna: enum sesuai dengan nilai int yang disimpan dalam database.
ToolmakerSteve
@ToolmakerSteve Anda benar bahwa pemetaan diperlukan. Tapi apakah Anda ingin meninggalkan pengkodean semacam itu ke beberapa ATAU mapper atau toolkit / perpustakaan?
Dilum Ranatunga
7

Menggunakan MyEnum enumValue = MyEnum.values()[x];

w.donahue
sumber
6

Inilah solusi yang saya rencanakan. Ini tidak hanya berfungsi dengan integer non-sekuensial, tetapi juga dapat bekerja dengan tipe data lain yang mungkin ingin Anda gunakan sebagai id yang mendasari nilai enum Anda.

public Enum MyEnum {
    THIS(5),
    THAT(16),
    THE_OTHER(35);

    private int id; // Could be other data type besides int
    private MyEnum(int id) {
        this.id = id;
    }

    public int getId() {
        return this.id;
    }

    public static Map<Integer, MyEnum> buildMap() {
        Map<Integer, MyEnum> map = new HashMap<Integer, MyEnum>();
        MyEnum[] values = MyEnum.values();
        for (MyEnum value : values) {
            map.put(value.getId(), value);
        }

        return map;
    }
}

Saya hanya perlu mengonversi id ke enum pada waktu tertentu (saat memuat data dari file), jadi tidak ada alasan bagi saya untuk menyimpan Peta di memori setiap saat. Jika Anda benar-benar membutuhkan peta agar dapat diakses setiap saat, Anda dapat selalu menyimpannya sebagai anggota statis kelas Enum Anda.

jkindwall
sumber
IMHO jika saya khawatir tentang penggunaan memori, saya akan secara dinamis membuat HashMap - seperti @ossys tetapi dengan kode yang berbeda ketika cache nol, kemudian menambahkan metode kedua clearCachedValuesketika Anda selesai menggunakannya (yang menetapkan bidang pribadi kembali ke nol). Saya menganggap MyEnum.fromInt(i)lebih mudah dipahami daripada melewati objek peta.
ToolmakerSteve
6

Jika itu membantu orang lain, opsi yang saya pilih, yang tidak tercantum di sini, menggunakan fungsionalitas Peta Guava :

public enum MyEnum {
    OPTION_1(-66),
    OPTION_2(32);

    private int value;
    private MyEnum(final int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }

    private static ImmutableMap<Integer, MyEnum> reverseLookup = 
            Maps.uniqueIndex(Arrays.asList(MyEnum.values())), MyEnum::getValue);

    public static MyEnum fromInt(final int id) {
        return reverseLookup.getOrDefault(id, OPTION_1);
    }
}

Dengan default yang dapat Anda gunakan null, Anda bisa throw IllegalArgumentExceptionatau Anda fromIntbisa mengembalikan Optional, perilaku apa pun yang Anda inginkan.

Chad Befus
sumber
3
Anda harus menyebutkan bahwa Anda menggunakan Jambu Biji. Atau Anda dapat menggunakan stream:Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
shmosel
1
Mungkin ingin mendefinisikan getValue()metode juga.
shmosel
@shosel Ups, saya melewatkan fungsi getValue, terima kasih. Mengetik versi generik ke dalam stackoverflow tidak selalu berjalan dengan baik. Menambahkan komentar tentang menggunakan Guava dengan tautan. Lebih suka metode Jambu ke stream.
Chad Befus
4

Anda dapat beralih lebih values()dari enum dan membandingkan nilai integer enum dengan yang diberikan idseperti di bawah ini:

public enum  TestEnum {
    None(0),
    Value1(1),
    Value2(2),
    Value3(3),
    Value4(4),
    Value5(5);

    private final int value;
    private TestEnum(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public static TestEnum  getEnum(int value){
        for (TestEnum e:TestEnum.values()) {
            if(e.getValue() == value)
                return e;
        }
        return TestEnum.None;//For values out of enum scope
    }
}

Dan gunakan seperti ini:
TestEnum x = TestEnum.getEnum(4);//Will return TestEnum.Value4
Saya harap ini membantu;)

Yashar Aliabbasi
sumber
3

Berdasarkan jawaban @ChadBefus dan komentar @shmosel, saya sarankan menggunakan ini. (Pencarian efisien, dan berfungsi di java murni> = 8)

import java.util.stream.Collectors;
import java.util.function.Function;
import java.util.Map;
import java.util.Arrays;

public enum MyEnum {
    OPTION_1(-66),
    OPTION_2(32);

    private int value;
    private MyEnum(final int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }

    private static Map<Integer, MyEnum> reverseLookup =
        Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));

    public static MyEnum fromInt(final int id) {
        return reverseLookup.getOrDefault(id, OPTION_1);
    }
    public static void main(String[] args) {
        System.out.println(fromInt(-66).toString());
    }
}
Yuki Inoue
sumber
0

Pilihan yang baik adalah menghindari konversi dari intmenjadienum : misalnya, jika Anda membutuhkan nilai maksimal, Anda dapat membandingkan x.ordinal () ke y.ordinal () dan mengembalikan x atau y secara bersamaan. (Anda mungkin perlu memesan ulang nilai-nilai Anda agar perbandingan tersebut bermakna.)

Jika itu tidak mungkin, saya akan menyimpan MyEnum.values()ke array statis.

18446744073709551615
sumber
1
Jika Anda mendapatkan int dari DB dan ingin mengubahnya menjadi Enum, yang saya pikir adalah tugas yang sangat umum, Anda akan memerlukan int -> konversi enum, IMO ..
Yuki Inoue
0

Ini adalah jawaban yang sama dengan dokter tetapi ini menunjukkan bagaimana cara menghilangkan masalah dengan array yang bisa berubah. Jika Anda menggunakan pendekatan semacam ini karena prediksi cabang terlebih dahulu jika akan memiliki efek sangat kecil ke nol dan seluruh kode hanya memanggil nilai array yang dapat diubah () hanya berfungsi sekali. Karena kedua variabel statis, mereka tidak akan menggunakan memori n * untuk setiap penggunaan enumerasi ini juga.

private static boolean arrayCreated = false;
private static RFMsgType[] ArrayOfValues;

public static RFMsgType GetMsgTypeFromValue(int MessageID) {
    if (arrayCreated == false) {
        ArrayOfValues = RFMsgType.values();
    }

    for (int i = 0; i < ArrayOfValues.length; i++) {
        if (ArrayOfValues[i].MessageIDValue == MessageID) {
            return ArrayOfValues[i];
        }
    }
    return RFMsgType.UNKNOWN;
}
Ali Akdurak
sumber
0
enum MyEnum {
    A(0),
    B(1);
    private final int value;
    private MyEnum(int val) {this.value = value;}
    private static final MyEnum[] values = MyEnum.values();//cache for optimization
    public static final getMyEnum(int value) { 
        try {
            return values[value];//OOB might get triggered
        } catch (ArrayOutOfBoundsException e) {
        } finally {
            return myDefaultEnumValue;
        }
    }
}
Zoso
sumber
@shmosel Saya percaya pengecualian jauh lebih baik jika batas di luar sangat sedikit.
Zoso
Apa yang mendasari kepercayaan Anda?
shmosel
0

Di Kotlin:

enum class Status(val id: Int) {
    NEW(0), VISIT(1), IN_WORK(2), FINISHED(3), CANCELLED(4), DUMMY(5);

    companion object {
        private val statuses = Status.values().associateBy(Status::id)

        fun getStatus(id: Int): Status? = statuses[id]
    }
}

Pemakaian:

val status = Status.getStatus(1)!!
CoolMind
sumber
0

Tulis implementasi ini. Ini memungkinkan untuk nilai yang hilang, nilai negatif dan menjaga kode konsisten. Peta juga di-cache. Menggunakan antarmuka dan membutuhkan Java 8.

Enum

public enum Command implements OrdinalEnum{
    PRINT_FOO(-7),
    PRINT_BAR(6),
    PRINT_BAZ(4);

    private int val;
    private Command(int val){
        this.val = val;
    }

    public int getVal(){
        return val;
    }

    private static Map<Integer, Command> map = OrdinalEnum.getValues(Command.class);
    public static Command from(int i){
        return map.get(i);
    }
}

Antarmuka

public interface OrdinalEnum{
    public int getVal();

    @SuppressWarnings("unchecked")
    static <E extends Enum<E>> Map<Integer, E> getValues(Class<E> clzz){
        Map<Integer, E> m = new HashMap<>();
        for(Enum<E> e : EnumSet.allOf(clzz))
            m.put(((OrdinalEnum)e).getVal(), (E)e);

        return m;
    }
}
Gerrit Brink
sumber