แทนที่ valueof () และ toString () ใน Java enum


122

ค่าใน my enumคือคำที่ต้องมีช่องว่าง แต่ enums ไม่สามารถมีช่องว่างในค่าได้ดังนั้นจึงรวมกันทั้งหมด ฉันต้องการลบล้างtoString()เพื่อเพิ่มช่องว่างเหล่านี้ในที่ที่ฉันบอกไป

ฉันยังต้องการให้ enum ระบุ enum ที่ถูกต้องเมื่อฉันใช้valueOf()กับสตริงเดียวกับที่ฉันเพิ่มช่องว่างเข้าไป

ตัวอย่างเช่น:

public enum RandomEnum
{
     StartHere,
     StopHere
}

โทรtoString()บนRandomEnumมีค่าเป็นสตริงผลตอบแทนStartHere "Start Here"โทรvalueof()ในสายเดียวกันที่ ( "Start Here") StartHereผลตอบแทนคุ้มค่า

ฉันจะทำเช่นนี้ได้อย่างไร?


2
เพิ่มแท็ก "Java" เพื่อรับความคิดเห็น / คำตอบเพิ่มเติม
Java42

อาจซ้ำกันของImplementing toString บน Java enums
Steve Chambers

คำตอบ:


197

คุณสามารถลองใช้รหัสนี้ เนื่องจากคุณไม่สามารถแทนที่valueOfวิธีการได้คุณจึงต้องกำหนดวิธีการที่กำหนดเอง ( getEnumในโค้ดตัวอย่างด้านล่าง) ซึ่งจะส่งคืนค่าที่คุณต้องการและเปลี่ยนให้ไคลเอ็นต์ของคุณใช้วิธีนี้แทน

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();
    }
}

4
getEnum สามารถย่อให้สั้นลงได้หากคุณทำสิ่งต่อไปนี้: v.getValue (). เท่ากับ (ค่า); การตรวจสอบค่าว่างสามารถละเว้นได้
rtcarlson

นอกจากนี้คุณยังสามารถสร้างแผนที่และนำกลับมาใช้ใหม่ได้อย่างเกียจคร้าน แต่ระวังปัญหาเรื่องเธรดขณะสร้าง
Maarten Bodewes

11
ใช้ไม่ได้ในสถานการณ์ที่คุณไม่สามารถเปลี่ยนการเรียกเมธอด valueOf () เป็น getValue () เช่นเมื่อคุณกำหนดฟิลด์บางฟิลด์ผ่านคำอธิบายประกอบไฮเบอร์เนตเพื่อแมปกับค่า enum
mmierins

จนกว่า Enums จะได้รับการแก้ไขใน Java @Bat มีโซลูชันที่เหมาะสมและเป็นมิตรกับ OO มากขึ้น ชื่อ () ไม่ควรถือเป็นที่สิ้นสุด
Andrew T Finnell

แทนคุณสามารถใช้ valueOf (RandomEnum.class, value);
Wojtek

22

ลองใช้ แต่ฉันไม่แน่ใจว่าจะใช้ได้ทุกที่ :)

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) {}
    }
}

18
ทำไมฉันเป็นคนเดียวที่ของแบบนั้นดูชั่วร้าย? ฉันหมายความว่ามันดูดีมาก แต่คนที่ไม่มีบริบทที่อ่านโค้ดนั้นส่วนใหญ่มักจะเป็น "WTF?"
Nicolas Guillaume

11
@NicolasGuillaume นั่นคือรหัสประเภทที่หากนำไปใช้งานจะต้องมีความคิดเห็นอธิบายว่าเหตุใดจึงอยู่ที่นั่นและปัญหาใดที่โปรแกรมเมอร์พยายามแก้ไข :-)
Ti Strga

9
อย่าจับข้อยกเว้นทั่วไปเพราะอาจเป็น OutOfMemory หรืออะไรก็ได้
crusy

2
นี่เป็นความคิดที่ผิดมหันต์ อย่างน้อยที่สุดคือความเป็นไปได้ที่จะเกิดข้อผิดพลาดโดยไม่มีสัญญาณบ่งชี้หรือการกู้คืน ตอนนี้ค่า "ชื่อ" และค่าสัญลักษณ์ในรหัส (เช่น A, B) ต่างกัน +2 เพื่อความฉลาด -200 เพราะฉลาดชะมัด ไม่มีทางที่ฉันจะอนุมัติการตรวจสอบโค้ดนี้
pedorro

1
@pedorro สิ่งนี้ดูเหมือนจะไม่น่ากลัวเท่าที่คุณทำออกมา ใครก็ตามที่เข้าใจว่าทุกสิ่งที่คณะกรรมการ Java ไม่ควรถูกนำมาใช้เนื่องจาก Gospel จะส่งผ่านสิ่งนี้ไปใน Code Review มันแย่ลงอย่างมากที่ต้องเขียนใหม่แล้วคงวิธียูทิลิตี้ Enum ทั้งหมดไว้ทั้งหมดเนื่องจาก name () ไม่สามารถแทนที่ได้ แทนที่จะจับข้อยกเว้นและกลืนมันควรโยน RuntimeException แล้วก็ไม่เป็นไร
Andrew T Finnell

14

คุณสามารถใช้แผนที่แบบคงที่ใน enum ของคุณที่แมป Strings กับ enum คงที่ ใช้ในวิธีคงที่ 'getEnum' สิ่งนี้จะข้ามความจำเป็นในการวนซ้ำผ่าน enums ทุกครั้งที่คุณต้องการรับหนึ่งจากค่า String

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;
    }
}

ตรวจสอบให้แน่ใจว่าการเริ่มต้นแบบคงที่ของแผนที่เกิดขึ้นใต้การประกาศค่าคงที่ enum

BTW - ประเภท 'ImmutableMap' นั้นมาจาก Google guava API และฉันขอแนะนำอย่างแน่นอนในกรณีเช่นนี้


แก้ไข - ตามความคิดเห็น:

  1. โซลูชันนี้ถือว่าค่าสตริงที่กำหนดแต่ละค่าไม่ซ้ำกันและไม่ใช่ค่าว่าง เนื่องจากผู้สร้าง enum สามารถควบคุมสิ่งนี้ได้และสตริงสอดคล้องกับค่า enum ที่ไม่ซ้ำกันและไม่เป็นค่าว่างจึงดูเหมือนเป็นข้อ จำกัด ที่ปลอดภัย
  2. ฉันเพิ่มเมธอด 'toSTring ()' ตามที่ถามในคำถาม

1
ฉันได้รับการโหวตลงคะแนนสองสามข้อเกี่ยวกับคำตอบนี้ - มีใครสนใจที่จะขยายความว่าทำไมคำตอบนี้ถึงไม่ดี? ฉันแค่อยากรู้ว่าผู้คนกำลังคิดอะไรอยู่
pedorro

สิ่งนี้จะล้มเหลวสำหรับสถานการณ์ที่มีค่าคงที่หลายค่าที่มี 'ค่า' เหมือนRestartHere("replay"), ReplayHere("replay")หรือไม่มี 'ค่า' เหมือนกันPauseHere, WaitHere(เช่น enum มีตัวสร้างเริ่มต้นเช่นprivate RandomEnum() { this.strVal = null; }
sactiw

1
คุณควรใช้ ImmutableMap.Builder แทนการสร้าง MutableMap เพื่อสร้าง + ImmutableMap :: copyOf
Bryan Correll

สิ่งนี้ดูน่าสนใจ แต่ถ้า enum มีค่าต่างกันเพียงเล็กน้อยก็จะใช้เวลาไม่นานในการทำซ้ำ มันอาจจะคุ้มค่าถ้า enum มีค่ามาก
Ben Thurley

13

การใช้งาน Java 8 เป็นอย่างไร? (ค่าว่างสามารถแทนที่ได้ด้วย Enum เริ่มต้นของคุณ)

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);
}

หรือคุณสามารถใช้:

...findAny().orElseThrow(NotFoundException::new);

2

ฉันไม่คิดว่าคุณจะได้รับ valueOf ("Start Here") ในการทำงาน แต่พอเว้นวรรค ... ลองทำตาม ...

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

2

ต่อไปนี้เป็นทางเลือกทั่วไปที่ดีสำหรับ 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);
}

การใช้งานที่compareTo()หรูหรากว่าequals()Strings หรือไม่?
Betlista

ความสง่างามไม่ใช่ปัญหา - ฉันยอมรับว่า.equals()จะใช้ได้ดี สามารถใช้ได้==ถ้าคุณต้องการ (แม้ว่าเหตุผลที่ดีที่คุณจะไม่ควรแทนที่ enum ด้วยคลาส)
ข้อยกเว้น

3
@exception ไม่เคยใช้ == สำหรับการเปรียบเทียบ String เนื่องจากจะทำการตรวจสอบความเท่าเทียมกันของวัตถุในขณะที่คุณต้องการคือการตรวจสอบความเท่าเทียมกันของเนื้อหาและสำหรับวิธีการใช้ equals () ของคลาส String
sactiw

2

คุณยังมีตัวเลือกในการนำไปใช้ใน enum ของคุณสิ่งนี้:

public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name){...}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.