Ganti nilai dari () dan toString () di enum Java

122

Nilai dalam enumkata saya adalah kata-kata yang perlu memiliki spasi di dalamnya, tetapi enum tidak boleh memiliki spasi dalam nilainya sehingga semuanya terkumpul. Saya ingin menimpatoString() untuk menambahkan spasi ini di mana saya perintahkan.

Saya juga ingin enum memberikan enum yang benar saat saya gunakan valueOf() string yang sama dengan yang saya tambahkan spasi.

Sebagai contoh:

public enum RandomEnum
{
     StartHere,
     StopHere
}

Panggilan toString()pada RandomEnumyang nilainya StartHerekembali string yang "Start Here". Panggil valueof()pada string yang sama ( "Start Here") mengembalikan nilai enumStartHere .

Bagaimana saya bisa melakukan ini?

WildBamaBoy
sumber
2
Tambahkan tag "Java" untuk mendapatkan lebih banyak komentar / jawaban.
Java42
kemungkinan duplikat Implementing toString di enum Java
Steve Chambers

Jawaban:

197

Anda dapat mencoba kode ini. Karena Anda tidak dapat mengganti valueOfmetode, Anda harus menentukan metode khusus ( getEnumdalam contoh kode di bawah) yang mengembalikan nilai yang Anda perlukan dan mengubah klien Anda untuk menggunakan metode ini.

public enum RandomEnum {

    StartHere("Start Here"),
    StopHere("Stop Here");

    private String value;

    RandomEnum(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    @Override
    public String toString() {
        return this.getValue();
    }

    public static RandomEnum getEnum(String value) {
        for(RandomEnum v : values())
            if(v.getValue().equalsIgnoreCase(value)) return v;
        throw new IllegalArgumentException();
    }
}
Jugal Shah
sumber
4
getEnum bisa dipersingkat jika Anda melakukan hal berikut: v.getValue (). equals (value); pemeriksaan nol dapat dihilangkan.
rtcarlson
Anda juga dapat dengan malas membuat peta dan menggunakannya kembali, tetapi waspadalah terhadap masalah threading saat membuatnya.
Maarten Bodewes
11
tidak berfungsi dalam skenario di mana Anda tidak dapat mengubah pemanggilan metode valueOf () ke getValue () misalnya ketika Anda mendefinisikan bidang tertentu melalui anotasi Hibernate untuk dipetakan ke nilai enum
mmierins
Hingga Enum diperbaiki di Java, @Bat memiliki solusi yang lebih tepat dan ramah OO. name () tidak boleh final.
Andrew T Finnell
sebagai ganti untuk Anda dapat menggunakan valueOf (RandomEnum.class, value);
Wojtek
22

Coba ini, tetapi saya tidak yakin itu akan berhasil di mana-mana :)

public enum MyEnum {
    A("Start There"),
    B("Start Here");

    MyEnum(String name) {
        try {
            Field fieldName = getClass().getSuperclass().getDeclaredField("name");
            fieldName.setAccessible(true);
            fieldName.set(this, name);
            fieldName.setAccessible(false);
        } catch (Exception e) {}
    }
}
Kelelawar
sumber
18
Mengapa aku satu-satunya yang terlihat jahat? Maksud saya ini terlihat sangat keren tetapi seseorang tanpa konteks yang membaca kode itu kemungkinan besar akan seperti "WTF?".
Nicolas Guillaume
11
@NicolasGuillaume Itulah jenis kode yang, jika diterapkan, akan sangat membutuhkan komentar yang menjelaskan mengapa kode itu ada dan masalah apa yang coba dipecahkan oleh programmer. :-)
Ti Strga
9
Jangan menangkap Exception generik, karena ini mungkin OutOfMemory atau apa pun
crusy
2
Ini ide yang buruk. Tidak sedikit di antaranya adalah kemungkinan kesalahan diam tanpa indikasi atau pemulihan. Juga sekarang nilai "nama" dan nilai simbolik dalam kode (mis. A, B) berbeda. +2 karena pintar. -200 karena sangat pintar. Tidak mungkin saya akan menyetujui tinjauan kode ini.
pedorro
1
@pedorro Ini tampaknya tidak seburuk yang Anda bayangkan. Siapapun yang mengerti bahwa semua yang dilakukan oleh komite Java tidak boleh dianggap sebagai Injil akan meneruskan ini dalam Tinjauan Kode. Jauh lebih buruk jika harus menulis ulang dan kemudian mempertahankan semua metode utilitas Enum semua karena name () tidak dapat diganti. Alih-alih menangkap pengecualian dan menelannya, RuntimeException harus dilemparkan dan ini baik-baik saja.
Andrew T Finnell
14

Anda dapat menggunakan Map statis di enum yang memetakan Strings ke konstanta enum. Gunakan dalam metode statis 'getEnum'. Ini menghilangkan kebutuhan untuk mengulang melalui enum setiap kali Anda ingin mendapatkannya dari nilai String-nya.

public enum RandomEnum {

    StartHere("Start Here"),
    StopHere("Stop Here");

    private final String strVal;
    private RandomEnum(String strVal) {
        this.strVal = strVal;
    }

    public static RandomEnum getEnum(String strVal) {
        if(!strValMap.containsKey(strVal)) {
            throw new IllegalArgumentException("Unknown String Value: " + strVal);
        }
        return strValMap.get(strVal);
    }

    private static final Map<String, RandomEnum> strValMap;
    static {
        final Map<String, RandomEnum> tmpMap = Maps.newHashMap();
        for(final RandomEnum en : RandomEnum.values()) {
            tmpMap.put(en.strVal, en);
        }
        strValMap = ImmutableMap.copyOf(tmpMap);
    }

    @Override
    public String toString() {
        return strVal;
    }
}

Pastikan saja inisialisasi statis peta terjadi di bawah deklarasi konstanta enum.

BTW - tipe 'ImmutableMap' itu berasal dari Google guava API, dan saya merekomendasikannya dalam kasus seperti ini.


EDIT - Per komentar:

  1. Solusi ini mengasumsikan bahwa setiap nilai string yang ditetapkan adalah unik dan bukan nol. Mengingat bahwa pembuat enum dapat mengontrol ini, dan bahwa string tersebut sesuai dengan nilai enum unik & non-null, ini tampak seperti pembatasan yang aman.
  2. Saya menambahkan metode 'toSTring ()' seperti yang diminta dalam pertanyaan
pedorro
sumber
1
Saya telah menerima beberapa suara negatif untuk jawaban ini - ada yang peduli untuk menjelaskan mengapa jawaban ini buruk? Saya hanya ingin tahu apa yang orang pikirkan.
pedorro
Ini akan gagal untuk skenario yang memiliki beberapa konstanta enum dengan 'nilai' yang sama RestartHere("replay"), ReplayHere("replay")atau tanpa 'nilai' sama sekali PauseHere, WaitHere(yaitu enum memiliki konstruktor default sepertiprivate RandomEnum() { this.strVal = null; }
sactiw
1
Anda harus menggunakan ImmutableMap.Builder daripada membuat MutableMap untuk membangun + ImmutableMap :: copyOf.
Bryan Correll
Ini terlihat menarik tetapi jika enum hanya memiliki beberapa nilai yang berbeda maka tidak akan butuh waktu lama untuk mengulanginya. Mungkin hanya sepadan jika enum memiliki banyak nilai.
Ben Thurley
13

Bagaimana dengan implementasi Java 8? (null dapat diganti dengan Enum default Anda)

public static RandomEnum getEnum(String value) {
    List<RandomEnum> list = Arrays.asList(RandomEnum.values());
    return list.stream().filter(m -> m.value.equals(value)).findAny().orElse(null);
}

Atau Anda bisa menggunakan:

...findAny().orElseThrow(NotFoundException::new);
corlaez
sumber
2

Saya tidak berpikir Anda akan mendapatkan valueOf ("Start Here") untuk bekerja. Tapi sejauh spasi ... coba yang berikut ...

static private enum RandomEnum {
    R("Start There"), 
    G("Start Here"); 
    String value;
    RandomEnum(String s) {
        value = s;
    }
}

System.out.println(RandomEnum.G.value);
System.out.println(RandomEnum.valueOf("G").value);

Start Here
Start Here
Java42
sumber
2

Berikut ini adalah alternatif umum yang bagus untuk valueOf ()

public static RandomEnum getEnum(String value) {
  for (RandomEnum re : RandomEnum.values()) {
    if (re.description.compareTo(value) == 0) {
      return re;
    }
  }
  throw new IllegalArgumentException("Invalid RandomEnum value: " + value);
}
pengecualian
sumber
apakah penggunaan compareTo()lebih elegan dari equals()pada Strings?
Betlista
Keanggunan bukan masalah - Saya setuju itu .equals()akan berfungsi dengan baik. Bisa digunakan ==jika Anda suka. (Meskipun alasan yang baik untuk tidak melakukannya adalah jika Anda mengganti enum dengan kelas.)
pengecualian
3
@exception TIDAK PERNAH MENGGUNAKAN == untuk perbandingan String karena ia akan melakukan pemeriksaan persamaan objek sementara Anda yang Anda butuhkan adalah pemeriksaan kesetaraan konten dan untuk itu gunakan metode equals () kelas String.
Sactiw
2

Anda masih memiliki opsi untuk mengimplementasikan dalam enum Anda ini:

public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name){...}
Andrey Lebedenko
sumber