วิธีรับค่า enum จากค่าสตริงใน Java


1984

ว่าฉันมี enum ซึ่งเป็นเพียงแค่

public enum Blah {
    A, B, C, D
}

และผมอยากจะหาค่า enum ของสตริง, ตัวอย่างซึ่งจะเป็น"A" Blah.Aเป็นไปได้อย่างไรที่จะทำเช่นนี้?

เป็นEnum.valueOf()วิธีที่ฉันต้องการหรือไม่ ถ้าเป็นเช่นนั้นฉันจะใช้มันได้อย่างไร

คำตอบ:


2258

ใช่จะทำให้คุณBlah.valueOf("A")Blah.A

โปรดทราบว่าชื่อจะต้องเป็นที่แน่นอนการแข่งขันรวมทั้งกรณี: Blah.valueOf("a")และทั้งโยนBlah.valueOf("A ")IllegalArgumentException

วิธีการคงที่valueOf()และvalues()ถูกสร้างขึ้นในเวลารวบรวมและไม่ปรากฏในซอร์สโค้ด พวกเขาปรากฏใน Javadoc แม้ว่า; ตัวอย่างเช่นDialog.ModalityTypeแสดงทั้งสองวิธี


100
สำหรับการอ้างอิงBlah.valueOf("A")วิธีการนี้ต้องคำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่และไม่ทนต่อช่องว่างภายนอกดังนั้นทางเลือกอื่นที่เสนอโดย @ JoséMi
Brett

3
@Michael Myers เนื่องจากคำตอบนี้ได้รับการโหวตมากที่สุดฉันควรเข้าใจหรือไม่ว่าเป็นวิธีปฏิบัติที่ดีในการกำหนดค่า enum และค่าสตริงให้เหมือนกันทุกประการ
เควินเมเรดิ ธ

4
@KevinMeredith: ถ้าคุณหมายถึงtoString()คุณค่าไม่ใช่ฉันจะไม่พูดอย่างนั้น name()คุณจะได้รับชื่อที่กำหนดจริงของค่าคงที่ enum เว้นแต่คุณจะแทนที่
Michael Myers

3
สิ่งที่คุณหมายถึงโดย "สร้างขึ้นในเวลารวบรวมและไม่ปรากฏในซอร์สโค้ด" ?
treesAreEverywhere

8
@treesAreEverywhere โดยเฉพาะอย่างยิ่งวิธีการเหล่านั้นจะถูกสร้างขึ้น (หรือสังเคราะห์ ) โดยคอมไพเลอร์ ที่เกิดขึ้นจริงenum Blah {...}ความหมายไม่ควรพยายามที่จะประกาศตัวของมันเองมิได้values valuesOfมันเหมือนกับวิธีที่คุณสามารถเขียน "AnyTypeName.class" แม้ว่าคุณจะไม่เคยประกาศตัวแปรสมาชิก "class" คอมไพเลอร์ทำให้มันใช้งานได้ทั้งหมด (คำตอบนี้อาจไม่เป็นประโยชน์กับคุณ 3 เดือนต่อมา แต่เพียงในกรณีที่.)
Ti Strga

864

วิธีแก้ไขอื่นหากข้อความไม่เหมือนกับค่าการแจงนับ:

public enum Blah {
    A("text1"),
    B("text2"),
    C("text3"),
    D("text4");

    private String text;

    Blah(String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }

    public static Blah fromString(String text) {
        for (Blah b : Blah.values()) {
            if (b.text.equalsIgnoreCase(text)) {
                return b;
            }
        }
        return null;
    }
}

396
throw new IllegalArgumentException("No constant with text " + text + " found")return nullจะดีกว่า
whiskinssierra

8
@whiskeysierra Jon Skeet คงไม่เห็นด้วย stackoverflow.com/questions/1167982/…
Sanghyun Lee

11
@ Sangdol คุณช่วยให้เรารู้ว่าทำไมการคืนโมฆะจึงดีกว่า?
whiskinssierra

57
@Sangdol โดยปกติแล้วมันเป็นสิ่งที่ดีที่จะตรวจสอบสิ่งที่ SUN - อุ๊ปส์ - Oracle กำลังทำในสถานการณ์เดียวกัน และในฐานะที่เป็นEnum.valueOf ()แสดงว่ามันเป็นวิธีที่ดีที่สุดในการโยนข้อยกเว้นในกรณีนี้ เพราะมันเป็นสถานการณ์ที่พิเศษ "การเพิ่มประสิทธิภาพประสิทธิภาพ" เป็นข้ออ้างที่ดีในการเขียนโค้ดที่อ่านไม่ได้ ;-)
raudi

5
คุณสามารถใช้คำอธิบายประกอบ @Nullable เพื่อทำให้ "อ่านได้" ;-)
JoséMi

121

นี่คือยูทิลิตี้ที่ดีที่ฉันใช้:

/**
 * A common method for all enums since they can't have another base class
 * @param <T> Enum type
 * @param c enum type. All enums must be all caps.
 * @param string case insensitive
 * @return corresponding enum, or null
 */
public static <T extends Enum<T>> T getEnumFromString(Class<T> c, String string) {
    if( c != null && string != null ) {
        try {
            return Enum.valueOf(c, string.trim().toUpperCase());
        } catch(IllegalArgumentException ex) {
        }
    }
    return null;
}

จากนั้นในชั้นเรียนของฉันฉันมักจะมีสิ่งนี้เพื่อบันทึกการพิมพ์บางส่วน:

public static MyEnum fromString(String name) {
    return getEnumFromString(MyEnum.class, name);
}

หาก enums ของคุณไม่ใช่ตัวพิมพ์ใหญ่ทั้งหมดเพียงแค่เปลี่ยนEnum.valueOfบรรทัด

ผมเสียดายที่ไม่สามารถใช้T.classสำหรับEnum.valueOfเป็นTจะถูกลบทิ้ง


176
บล็อก catch อันว่างเปล่านั้นทำให้ฉันถั่วจริงๆ
whiskinssierra

32
@LazloBonin: มีข้อยกเว้นสำหรับเงื่อนไขพิเศษไม่ใช่สำหรับโฟลว์การควบคุม รับตัวเองสำเนาของที่มีประสิทธิภาพ Java
Reinstate Monica - M. Schröder

10
หาก Java API ที่คุณต้องการใช้มีข้อยกเว้นและคุณไม่ต้องการให้โค้ดของคุณมีรหัสคุณสามารถกลืนข้อยกเว้นเช่นนี้หรือเขียนตรรกะใหม่ตั้งแต่ต้นเพื่อไม่ให้มีข้อยกเว้นเกิดขึ้นในตอนแรก การกลืนยกเว้นเป็นความชั่วร้ายที่น้อยกว่า
Nate CK

47
ที่น่ากลัว! เสมอเสมอจับข้อยกเว้นที่คุณสามารถจัดการกับพวกเขา ตัวอย่างข้างต้นเป็นตัวอย่างที่สมบูรณ์แบบวิธีการที่จะไม่ทำมัน ทำไม? ดังนั้นจึงส่งกลับค่า NULL จากนั้นผู้เรียกต้องตรวจสอบกับ NULL หรือส่ง NPE หากผู้โทรรู้วิธีจัดการกับสถานการณ์การทำ if vs. try-catch อาจดูดีกว่านี้เล็กน้อยแต่ถ้าเขาจัดการไม่ได้เขาจะต้องผ่าน null อีกครั้งและผู้โทรของผู้โทรอีกครั้งจะต้องตรวจสอบกับ NULL ฯลฯ เป็นต้น
raudi

10
เพื่อความเป็นธรรมต่อโซลูชันข้างต้นมีกรณีที่ต้องใช้คุณคืน null แทนการขว้าง IllegalArgumentException และแยกการไหลของโปรแกรมของคุณตัวอย่างเช่นการแม็ป enums ระหว่าง schema บริการเว็บและสกีมาฐานข้อมูล -to- หนึ่ง อย่างไรก็ตามฉันยอมรับว่าบล็อก catch ไม่ควรปล่อยว่างไว้ ใส่รหัสเช่น log.warn หรือบางอย่างเพื่อการติดตาม
Adrian M

101

ใช้รูปแบบจาก Joshua Bloch, Java ที่มีประสิทธิภาพ :

(ลดความซับซ้อนลงอย่างง่าย)

enum MyEnum {
    ENUM_1("A"),
    ENUM_2("B");

    private String name;

    private static final Map<String,MyEnum> ENUM_MAP;

    MyEnum (String name) {
        this.name = name;
    }

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

    // Build an immutable map of String name to enum pairs.
    // Any Map impl can be used.

    static {
        Map<String,MyEnum> map = new ConcurrentHashMap<String, MyEnum>();
        for (MyEnum instance : MyEnum.values()) {
            map.put(instance.getName(),instance);
        }
        ENUM_MAP = Collections.unmodifiableMap(map);
    }

    public static MyEnum get (String name) {
        return ENUM_MAP.get(name);
    }
}

ดูเพิ่มเติมที่:

ตัวอย่าง Oracle Java ที่ใช้ Enum และ Map ของอินสแตนซ์

ลำดับการดำเนินการของบล็อกคงที่ในประเภท Enum

ฉันจะค้นหา Java enum จากค่า String ได้อย่างไร


4
ถ้า Joshua Bloch พูดแล้วนี่เป็นวิธีเดียวที่จะไป :-) มันเป็นความอัปยศที่ฉันต้องเลื่อนลงมาที่นี่เสมอ
dermoritz

11
นี่เป็นเรื่องที่ง่ายขึ้นใน Java 8 อย่างที่คุณสามารถทำได้: Stream.of(MyEnum.values()).collect(toMap(Enum::name, identity()))ฉันยังแนะนำให้ใช้ toString () (ส่งผ่านผ่าน Constructor) และใช้ชื่อนั้นแทนชื่อโดยเฉพาะถ้า Enum เชื่อมโยงกับข้อมูลที่สามารถทำให้เป็นอนุกรมได้ โซนาร์แบบพอดี
Novaterata

1
Java 8 แน่นอนสามารถ / จะเปลี่ยนคำตอบ (ดีกว่า) จำนวนมากในฟอรั่มนี้ ไม่แน่ใจเกี่ยวกับการมีหาง (Sonar) กระดิกสุนัข (รหัสสมัคร) แม้ว่า
Darrell Teague

3
หากคุณกำลังจะไปใส่ไว้ในต่อไปแล้วมีประโยชน์ที่จะเริ่มต้นด้วยไม่มีunmodifiableMap ใช้เพียงConcurrentHashMap HashMap(ถ้าคุณมี Guava ImmutableMapแล้วฉันขอแนะนำให้แทน!)
Daniel Pryden

9
การกำหนดค่าเริ่มต้นคงที่จะถูกซิงโครไนซ์โดยเนื้อแท้ดังนั้นจึงไม่มีเหตุผลที่จะใช้ConcurrentHashMapที่นี่ที่ซึ่งแผนที่ไม่เคยถูกแก้ไขหลังจากการเริ่มต้น ดังนั้นทำไมแม้เช่นตัวอย่างใน JLS HashMapที่ตัวเองใช้เป็นประจำ
Radiodef

74

คุณควรระมัดระวังกรณีของคุณ ให้ฉันอธิบาย: Blah.valueOf("A")ทำงาน แต่Blah.valueOf("a")จะไม่ทำงาน จากนั้นBlah.valueOf("a".toUpperCase(Locale.ENGLISH))ก็จะทำงานอีกครั้ง

แก้ไข
เปลี่ยนtoUpperCaseเป็นtoUpperCase(Locale.ENGLISH)อ้างอิงจากtc ความคิดเห็นและเอกสาร java

edit2 บน Android ที่คุณควรใช้Locale.USเป็นsulai ชี้ให้เห็น


6
ระวังสถานที่เริ่มต้น!
tc

3
สำหรับผู้ใช้ Android ของคุณฉันอยากจะชี้ให้เห็นว่าเอกสาร Androidสนับสนุนการใช้Locale.USอินพุต / เอาต์พุตของเครื่องที่สามารถอ่านได้อย่างชัดเจน
sulai

2
@Tototot ใช่น่าเสียดาย ตุรกีเป็นตัวอย่างที่ดี รวมสิ่งนี้กับการจัดการชุดอักขระเริ่มต้นที่เสียหายของ Java (ค่าเริ่มต้นเป็นภาษาละตินบน Windows แทนที่จะเป็น Unicode) และคุณจะพบว่าเกือบจะไม่ปลอดภัยที่จะใช้วิธีการรุ่นเริ่มต้นที่ยอมรับชุดอักขระหรือภาษา คุณควรกำหนดอย่างชัดเจนเกือบทุกครั้ง
Stijn de Witt

ไม่แน่ใจว่าชุดอักขระ "เริ่มต้น" และอื่น ๆ ของ Java เป็น "แตก" ต่อ se แต่ได้รับอนุญาตให้ใช้ค่าเริ่มต้นเป็น UTF-8 แทนการแทนที่ (ซึ่งควรทำเสมอเพื่อให้ชัดเจน) จะทำให้ระบบที่ดีกว่า แนวคิดชุดอักขระ
Darrell Teague

38

นี่คือวิธีการที่สามารถทำได้สำหรับ Enum ใด ๆ และไม่คำนึงถึงขนาดตัวพิมพ์

/** 
 * Finds the value of the given enumeration by name, case-insensitive. 
 * Throws an IllegalArgumentException if no match is found.  
 **/
public static <T extends Enum<T>> T valueOfIgnoreCase(
        Class<T> enumeration, String name) {

    for (T enumValue : enumeration.getEnumConstants()) {
        if (enumValue.name().equalsIgnoreCase(name)) {
            return enumValue;
        }
    }

    throw new IllegalArgumentException(String.format(
        "There is no value with name '%s' in Enum %s",
        name, enumeration.getName()
    ));
}

รูปแบบนี้ทำอย่างถูกต้อง: equalsIgnoreCaseเป็นวิธีที่จะไป +1
Stijn de Witt

ชอบความรู้สึกตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ แต่ ... ชอบ Enums มากกว่า (แบบสุ่ม) การกำหนดสตริงสำหรับคีย์และ ... เล็กน้อย แต่การวนซ้ำนั้นมีประสิทธิภาพน้อยกว่าสำหรับการค้นหาแบบซ้ำ ๆ ดังนั้นนัยของ EnumMap และคณะ
Darrell Teague

มันไม่ทำงาน! ฉันเปลี่ยน equalsIgnoreCase เป็นเท่ากับสำหรับวัตถุประสงค์ของฉัน รหัสล้มเหลวแม้ว่าอินพุตทั้งสองจะเท่ากับเท่ากันทุกประการ
MasterJoe2

36

การใช้Blah.valueOf(string)ดีที่สุด แต่คุณก็สามารถใช้ได้Enum.valueOf(Blah.class, string)เช่นกัน


1
คำนึงถึงขนาดตัวพิมพ์ไม่ช่วย!
Murtaza Kanchwala

@MurtazaKanchwala คุณช่วยอธิบายความคิดเห็นของคุณได้ไหม? คุณพยายามจะทำอะไร?
Peter Lawrey

2
สวัสดี @PeterLawrey ฉันกำลังพยายามดึง Enum จาก String สาธารณะ enum ObjectType {PERSON ("บุคคล") สาธารณะ String พารามิเตอร์ชื่อ; ObjectType (String parameterName) {this.parameterName = parameterName; } public String getParameterName () {return this.parameterName; } ObjectType สาธารณะคงที่ fromString (String parameterName) {ถ้า (parameterName! = null) {สำหรับ (ObjectType objType: ObjectType.values ​​()) {ถ้า (parameterName.equalsIgnoreCase (objType.parameterName)) {กลับ objType; }}} คืนค่า null; }}
Murtaza Kanchwala

34

ใน Java 8 หรือใหม่กว่าโดยใช้Streams :

public enum Blah
{
    A("text1"),
    B("text2"),
    C("text3"),
    D("text4");

    private String text;

    Blah(String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }

    public static Optional<Blah> fromText(String text) {
        return Arrays.stream(values())
          .filter(bl -> bl.text.equalsIgnoreCase(text))
          .findFirst();
    }
}

ไม่แน่ใจว่านี่เป็นคำตอบที่ดีกว่านี้ สตรีมในกรณีนี้คือตัววนซ้ำแบบเธรดเดียวเหมือนกับค่าอื่น ๆ ในทุกค่าและมีประสิทธิภาพน้อยกว่าการค้นหาแผนที่ สตรีมมีค่ามากขึ้นในบริบทแบบมัลติเธรดที่เช่นการดำเนินการขนานของไฟล์ข้อความที่คั่นด้วยบรรทัดใหม่สามารถปรับปรุงประสิทธิภาพได้
Darrell Teague

28

หากคุณไม่ต้องการเขียนโปรแกรมอรรถประโยชน์ของคุณเองให้ใช้ Google ห้องสมุด:

Enums.getIfPresent(Blah.class, "A")

ซึ่งแตกต่างจากฟังก์ชั่นจาวาในตัวมันช่วยให้คุณตรวจสอบว่า A มีอยู่ใน Blah และไม่ส่งข้อยกเว้น


7
ส่วนที่น่าเศร้าคือสิ่งนี้จะส่งคืนตัวเลือก google และไม่ใช่ตัวเลือก java
javaProgrammer

จริง Exoected แม้ว่า Google และ Netflix มี libs Java ที่ยอดเยี่ยม ในกรณีที่มีการทับซ้อนกับคลาส Java catch-up ที่นำมาใช้ในเวอร์ชันที่ใหม่กว่าย่อมทำให้เกิดปัญหา ชนิดต้องอยู่ในหนึ่ง lib ผู้ขาย
Darrell Teague

26

2 เซนต์ของฉันที่นี่: ใช้ Java8 Streams + ตรวจสอบสตริงที่แน่นอน:

public enum MyEnum {
    VALUE_1("Super"),
    VALUE_2("Rainbow"),
    VALUE_3("Dash"),
    VALUE_3("Rocks");

    private final String value;

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

    /**
     * @return the Enum representation for the given string.
     * @throws IllegalArgumentException if unknown string.
     */
    public static MyEnum fromString(String s) throws IllegalArgumentException {
        return Arrays.stream(MyEnum.values())
                .filter(v -> v.value.equals(s))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("unknown value: " + s));
    }
}

** แก้ไข **

เปลี่ยนชื่อฟังก์ชั่นเป็นfromString()ตั้งแต่ตั้งชื่อโดยใช้การประชุมนั้นคุณจะได้รับประโยชน์จากภาษาจาวาเอง ตัวอย่างเช่น:

  1. การแปลงประเภทโดยตรงที่คำอธิบายประกอบ HeaderParam

1
อีกทางเลือกหนึ่งเพื่อให้คุณสามารถเขียนswitchบล็อกที่อ่านได้มากขึ้นคุณสามารถ.orElse(null)แทน.orElseThrow()เพื่อให้คุณสามารถเขียนรหัสข้อยกเว้นในdefaultประโยค - และรวมถึงข้อมูลที่เป็นประโยชน์มากขึ้นเมื่อจำเป็น และเพื่อให้ผ่อนปรนมากขึ้นคุณสามารถใช้v -> Objects.equals(v.name, s == null ? "" : s.trim().toUpperCase())
Adam

หรือเพียงแค่กลับมาOptionalจากการfindFirst()ช่วยให้ผู้ใช้ในการตัดสินใจว่าเขาต้องการ.orElse(null), orElseThrow()หรืออะไรก็ตาม ....
user85421

1
การประกาศ a public static MyEnum valueOf(String)เป็นข้อผิดพลาดในการรวบรวมเนื่องจากเป็นเช่นเดียวกับที่กำหนดไว้โดยปริยายดังนั้นคำตอบที่เก่ากว่าของคุณจึงดีกว่า ( jls , ideone )
Radiodef

ในตัวเลือกของฉันจะดีกว่าเพื่อหลีกเลี่ยงข้อยกเว้นและใช้ตัวเลือก ถัดจากนั้นเราควรโมฆะโมฆะและใช้ตัวเลือกแทนเช่นกัน
Hans Schreuder

อีกครั้งโปรดจำไว้ว่าแม้ว่าโค้ดที่ดูน้อยหรือดีกว่า ... การใช้งานแบบสตรีมเช่นนี้เป็นเพียงตัววนซ้ำในทุกค่าเมื่อเทียบกับการค้นหาแผนที่ (ประสิทธิภาพน้อยกว่า)
Darrell Teague

16

คุณอาจต้อง:

public enum ObjectType {
    PERSON("Person");

    public String parameterName;

    ObjectType(String parameterName) {
        this.parameterName = parameterName;
    }

    public String getParameterName() {
        return this.parameterName;
    }

    //From String method will return you the Enum for the provided input string
    public static ObjectType fromString(String parameterName) {
        if (parameterName != null) {
            for (ObjectType objType : ObjectType.values()) {
                if (parameterName.equalsIgnoreCase(objType.parameterName)) {
                    return objType;
                }
            }
        }
        return null;
    }
}

นอกจากนี้อีกหนึ่ง:

   public static String fromEnumName(String parameterName) {
        if (parameterName != null) {
            for (DQJ objType : DQJ.values()) {
                if (parameterName.equalsIgnoreCase(objType.name())) {
                    return objType.parameterName;
                }
            }
        }
        return null;
    }

สิ่งนี้จะคืนค่าให้คุณโดยชื่อ Enum ที่ถูกทำให้เป็นสตริงสำหรับเช่นถ้าคุณให้ "PERSON" ใน fromEnumName มันจะส่งคืนค่า Enum เช่น "บุคคล"


13

อีกวิธีหนึ่งในการทำเช่นนี้โดยใช้วิธีคงที่โดยนัยname()ของ Enum ชื่อจะส่งกลับสตริงที่แน่นอนที่ใช้ในการสร้าง enum ที่สามารถใช้ในการตรวจสอบกับสตริงที่ระบุ:

public enum Blah {

    A, B, C, D;

    public static Blah getEnum(String s){
        if(A.name().equals(s)){
            return A;
        }else if(B.name().equals(s)){
            return B;
        }else if(C.name().equals(s)){
            return C;
        }else if (D.name().equals(s)){
            return D;
        }
        throw new IllegalArgumentException("No Enum specified for this string");
    }
}

การทดสอบ:

System.out.println(Blah.getEnum("B").name());

//it will print B  B

Inspiration: 10 ตัวอย่าง Enum ใน Java


7
นี่คือสิ่งที่valueOfคุณต้องการ วิธีการแบบคงที่นี้ไม่ได้ให้อะไรพิเศษยกเว้นและทั้งหมด ดังนั้นโครงสร้าง if / else จะมีอันตรายมาก ... ค่าคงที่ enum ใหม่ใด ๆ ที่เพิ่มเข้ามาจะทำให้วิธีการนี้พังโดยไม่มีการเปลี่ยนแปลง
YoYo

ลองพิจารณาตัวอย่างนี้ว่าเราจะใช้ valueOf สำหรับการค้นหาแบบ case-insensitive ได้อย่างไรหรือวิธีที่เราสามารถหลีกเลี่ยงได้นั้นเป็นข้อยกเว้นและใช้ aliases เพื่อให้ชื่อทางเลือก: stackoverflow.com/a/12659023/744133
YoYo

2
name()ไม่คงที่
nrubin29

10

วิธีการแก้ปัญหาโดยใช้ห้องสมุด Guava เมธอด getPlanet () ไม่คำนึงถึงขนาดตัวพิมพ์ดังนั้น getPlanet ("MerCUrY") จะส่งคืน Planet.MERCURY

package com.universe.solarsystem.planets;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Enums;
import com.google.common.base.Optional;

//Pluto and Eris are dwarf planets, who cares!
public enum Planet {
   MERCURY,
   VENUS,
   EARTH,
   MARS,
   JUPITER,
   SATURN,
   URANUS,
   NEPTUNE;

   public static Planet getPlanet(String name) {
      String val = StringUtils.trimToEmpty(name).toUpperCase();
      Optional <Planet> possible = Enums.getIfPresent(Planet.class, val);
      if (!possible.isPresent()) {
         throw new IllegalArgumentException(val + "? There is no such planet!");
      }
      return possible.get();
   }
}

8

เพื่อเพิ่มคำตอบก่อนหน้านี้และพูดถึงการอภิปรายบางส่วนเกี่ยวกับค่า Null และ NPE ฉันใช้ Guava Optionals เพื่อจัดการกับกรณีที่ขาด / ไม่ถูกต้อง วิธีนี้ใช้งานได้ดีสำหรับการแยกวิเคราะห์ URI / พารามิเตอร์

public enum E {
    A,B,C;
    public static Optional<E> fromString(String s) {
        try {
            return Optional.of(E.valueOf(s.toUpperCase()));
        } catch (IllegalArgumentException|NullPointerException e) {
            return Optional.absent();
        }
    }
}

สำหรับผู้ที่ไม่ทราบต่อไปนี้เป็นข้อมูลเพิ่มเติมเกี่ยวกับการหลีกเลี่ยงค่า null ด้วยตัวเลือก: https://code.google.com/p/guava-l ไลบรารี/wiki/UsingAndAvoidingNullExplained #Optional


นี่เป็นคำตอบที่ดีมากสำหรับรูปแบบที่เกี่ยวข้องและการใช้ตัวเลือกภายใน Enum - การใช้ประโยชน์จากความจริงที่ว่า Enums เป็นคลาสเช่นกัน nulls ที่ส่งคืนจากเมธอดทำให้ buggy สร้าง (NPE ในตำแหน่งที่ไม่รู้จักในเชน Fluent ของการเรียกเมธอด)
Darrell Teague

8

ใน Java 8 รูปแบบแผนที่แบบคงที่นั้นง่ายยิ่งขึ้นและเป็นวิธีที่ฉันไว้ล่วงหน้า หากคุณต้องการใช้ Enum กับ Jackson คุณสามารถแทนที่ toString และใช้สิ่งนั้นแทนชื่อได้แล้วใส่คำอธิบายประกอบด้วย@JsonValue

public enum MyEnum {
    BAR,
    BAZ;
    private static final Map<String, MyEnum> MAP = Stream.of(MyEnum.values()).collect(Collectors.toMap(Enum::name, Function.identity()));
    public static MyEnum fromName(String name){
        return MAP.get(name);
    }
}

public enum MyEnumForJson {
    BAR("bar"),
    BAZ("baz");
    private static final Map<String, MyEnumForJson> MAP = Stream.of(MyEnumForJson.values()).collect(Collectors.toMap(Object::toString, Function.identity()));
    private final String value;

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

    @JsonValue
    @Override
    public String toString() {
        return value;
    }

    public static MyEnumForJson fromValue(String value){
        return MAP.get(value);
    }
}

แจ็กสันเป็นการใช้งาน JSON (JavaScript Object Notation) คำถามเดิมไม่มีส่วนเกี่ยวข้องกับ JSON
Darrell Teague

ส่วน JSON เป็นเพียงสิ่งโบนัสที่ฉันพบว่าเกี่ยวข้องในขณะนั้นเนื่องจากการรับ Enum จากสตริงนั้นเป็นประเภทของการดีซีเรียลไลเซชันและ JSON / Jackson อาจเป็นวิธีแก้ปัญหาซีเรียลไลซ์เซชั่นที่เป็นที่นิยมที่สุด
Novaterata

ทำความเข้าใจ แต่จากแง่มุมการกลั่นกรอง - มันไม่ได้มีส่วนช่วยตอบคำถามของ OP ดังนั้นเพียงแค่พยายามช่วยกำหนดบริบทที่นั่น JSON เป็นวิธีที่จะไปแปลงวัตถุให้อยู่ในรูปแบบมาตรฐานใน Java โดยที่ Jackson เป็นห้องสมุดที่ยอดเยี่ยม
ดาร์เรล Teague

6
public static MyEnum getFromValue(String value) {
    MyEnum resp = null;
    MyEnum nodes[] = values();
    for(int i = 0; i < nodes.length; i++) {
        if(nodes[i].value.equals(value)) {
            resp = nodes[i];
            break;
        }
    }
    return resp;
}

ลองดูที่ลิงค์นี้สำหรับคำแนะนำในการตอบและถามคำถามใน stackoverflow.com: stackoverflow.com/faq
bakoyaro

1
นั่นมากหรือน้อยกว่าเช่นเดียวกับคำตอบของโฮเซมิ
Rup

6

วิธี O (1) เป็นแรงบันดาลใจจากรหัสที่สร้างขึ้นอย่างรวดเร็วซึ่งใช้ hashmap

public enum USER {
        STUDENT("jon",0),TEACHER("tom",1);

        private static final Map<String, Integer> map = new HashMap<>();

        static {
                for (USER user : EnumSet.allOf(USER.class)) {
                        map.put(user.getTypeName(), user.getIndex());
                }
        }

        public static int findIndexByTypeName(String typeName) {
                return map.get(typeName);
        }

        private USER(String typeName,int index){
                this.typeName = typeName;
                this.index = index;
        }
        private String typeName;
        private int index;
        public String getTypeName() {
                return typeName;
        }
        public void setTypeName(String typeName) {
                this.typeName = typeName;
        }
        public int getIndex() {
                return index;
        }
        public void setIndex(int index) {
                this.index = index;
        }

}

โปรดสังเกตว่าตัวระบุศูนย์ (0) และหนึ่ง (1) ไม่จำเป็น ค่า Enum () วิธีการจะส่งกลับสมาชิกในลำดับเดียวกันกับรหัส ดังนั้นรายการแรกจะเป็นเลขลำดับศูนย์ลำดับที่สองเป็นต้น
Darrell Teague

6

Enum มีประโยชน์มากฉันใช้Enumจำนวนมากเพื่อเพิ่มคำอธิบายสำหรับบางฟิลด์ในภาษาต่าง ๆ ดังตัวอย่างต่อไปนี้:

public enum Status {

    ACT(new String[] { "Accepted", "مقبول" }),
    REJ(new String[] { "Rejected", "مرفوض" }),
    PND(new String[] { "Pending", "في الانتظار" }),
    ERR(new String[] { "Error", "خطأ" }),
    SNT(new String[] { "Sent", "أرسلت" });

    private String[] status;

    public String getDescription(String lang) {
        return lang.equals("en") ? status[0] : status[1];
    }

    Status(String[] status) {
        this.status = status;
    }
}

จากนั้นคุณสามารถดึงคำอธิบายแบบไดนามิกโดยใช้รหัสภาษาที่ส่งผ่านไปยังgetDescription(String lang)วิธีตัวอย่างเช่น

String statusDescription = Status.valueOf("ACT").getDescription("en");

1
ตัวอย่างที่ดีด้วยการผลักดัน Enums ต่อไป จะทำให้การเข้ารหัสภาษาโดยใช้ชื่อคงมาตรฐานและการค้นหาในแผนที่ แต่ยัง ... ตัวอย่างที่ดีของการมี enum ที่มีแท็กที่แตกต่างกันสำหรับสิ่งที่เป็นค่าตรรกะเดียวกัน
Darrell Teague

5

เกี่ยวกับอะไร

public enum MyEnum {
    FIRST,
    SECOND,
    THIRD;

    public static Optional<MyEnum> fromString(String value){
        try{
            return Optional.of(MyEnum.valueOf(value));
        }catch(Exception e){
            return Optional.empty();
        }
    }
}

4

java.lang.Enum กำหนดวิธีการที่มีประโยชน์หลายวิธีซึ่งมีอยู่ในประเภทการแจงนับทั้งหมดใน Java:

  • คุณสามารถใช้name()วิธีการรับชื่อค่าคง Enum ใด ๆ สตริงตัวอักษรที่ใช้ในการเขียนค่าคง Enum เป็นชื่อของพวกเขา
  • values()วิธีการในทำนองเดียวกันสามารถใช้ในการรับอาร์เรย์ของค่าคงที่ Enum ทั้งหมดจากประเภท Enum
  • และสำหรับคำถามที่ถามคุณสามารถใช้valueOf()วิธีการแปลงค่าคงที่ String เป็น Enum ใน Java ดังที่แสดงด้านล่าง
public class EnumDemo06 {
    public static void main(String args[]) {
        Gender fromString = Gender.valueOf("MALE");
        System.out.println("Gender.MALE.name() : " + fromString.name());
    }

    private enum Gender {
        MALE, FEMALE;
    }
}

Output:
Gender.MALE.name() : MALE

ในโค้ดนี้valueOf()วิธีการส่งกลับ Enum คง Gender.MALE "MALE"เรียกชื่อในผลตอบแทนที่


4

ไลบรารีcommons-langของ Apache มีฟังก์ชั่นคงที่org.apache.commons.lang3.EnumUtils.getEnumซึ่งจะแมปสตริงกับประเภท Enum ของคุณ คำตอบเดียวกับ Geoffreys แต่ทำไมคุณต้องม้วนตัวเองเมื่อมันอยู่ในป่าแล้ว


1
แสดงความคิดเห็นอย่างยุติธรรม (DRY) แต่ ... ในขณะที่เนื้อหา Apache Commons ส่วนใหญ่ดีมากฉันได้พบข้อบกพร่องหลายประการและรูปแบบการต่อต้านตัวเองในฐานนั้น ดังนั้นการอ้างว่าการใช้งานของโจชัวโบลชอาจเป็นฐานที่แข็งแกร่งขึ้น ลงไปแล้วต้องทบทวนรหัส Apache เพื่อทราบว่ามีคนนำไปใช้อย่างไร ถ้ามีคนกล่าวว่าดั๊กเลอาห์ผู้โด่งดังผู้เขียน Java เห็นพ้องด้วย ... ฉันจะเชื่อใจมันโดยปริยาย
Darrell Teague

4

การเพิ่มคำตอบให้คะแนนสูงสุดด้วยประโยชน์ที่เป็นประโยชน์ ...

valueOf() โยนข้อยกเว้นที่แตกต่างกันสองกรณีในกรณีที่ไม่ชอบอินพุต

  • IllegalArgumentException
  • NullPointerExeption

หากความต้องการของคุณเป็นเช่นนั้นคุณไม่มีการรับประกันว่า String ของคุณจะจับคู่ค่า enum อย่างแน่นอนตัวอย่างเช่นถ้าข้อมูล String มาจากฐานข้อมูลและอาจมี enum เวอร์ชันเก่าคุณจะต้องจัดการสิ่งเหล่านี้ บ่อยครั้ง...

ดังนั้นนี่เป็นวิธีที่นำกลับมาใช้ใหม่ได้ฉันเขียนซึ่งช่วยให้เรากำหนด Enum เริ่มต้นที่จะถูกส่งคืนถ้า String ที่เราผ่านไม่ตรงกัน

private static <T extends Enum<T>> T valueOf( String name , T defaultVal) {
        try {
            return Enum.valueOf(defaultVal.getDeclaringClass() , name);
        } catch (IllegalArgumentException | NullPointerException e) {
            return defaultVal;
        }
    }

ใช้แบบนี้:

public enum MYTHINGS {
    THINGONE,
    THINGTWO
}

public static void main(String [] asd) {
  valueOf("THINGTWO" , MYTHINGS.THINGONE);//returns MYTHINGS.THINGTWO
  valueOf("THINGZERO" , MYTHINGS.THINGONE);//returns MYTHINGS.THINGONE
}

2

ในฐานะที่เป็นswitchรุ่นที่ยังไม่ได้รับการกล่าวถึงยังฉันแนะนำมัน (reum OP ของ enum):

  private enum Blah {
    A, B, C, D;

    public static Blah byName(String name) {
      switch (name) {
        case "A":
          return A;
        case "B":
          return B;
        case "C":
          return C;
        case "D":
          return D;
        default:
          throw new IllegalArgumentException(
            "No enum constant " + Blah.class.getCanonicalName() + "." + name);
      }
    }
  }

เนื่องจากสิ่งนี้ไม่ได้ให้คุณค่าเพิ่มเติมใด ๆ กับvalueOf(String name)วิธีการนี้จึงเหมาะสมที่จะกำหนดวิธีการเพิ่มเติมหากเราต้องการมีพฤติกรรมที่แตกต่าง หากเราไม่ต้องการยกระดับIllegalArgumentExceptionเราสามารถเปลี่ยนการใช้งานเป็น:

  private enum Blah {
    A, B, C, D;

    public static Blah valueOfOrDefault(String name, Blah defaultValue) {
      switch (name) {
        case "A":
          return A;
        case "B":
          return B;
        case "C":
          return C;
        case "D":
          return D;
        default:
          if (defaultValue == null) {
            throw new NullPointerException();
          }
          return defaultValue;
      }
    }
  }

โดยการให้ค่าเริ่มต้นที่เราให้สัญญาของEnum.valueOf(String name)โดยไม่ต้องขว้างปาIllegalArgumentException ในลักษณะที่ว่าในกรณีใด ๆnullจะถูกส่งกลับ ดังนั้นเราจึงโยนNullPointerExceptionถ้ามีชื่อเป็นnullและในกรณีของdefaultถ้าเป็นdefaultValue nullนั่นเป็นวิธีการvalueOfOrDefaultทำงาน

วิธีนี้ใช้การออกแบบMap-Interface ซึ่งมีวิธีการMap.getOrDefault(Object key, V defaultValue)เป็นของ Java 8


1

ยูทิลิตี้อื่นที่จับภาพในทางกลับกัน ใช้ค่าที่ระบุว่า Enum ไม่ใช่จากชื่อ

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.EnumSet;

public class EnumUtil {

    /**
     * Returns the <code>Enum</code> of type <code>enumType</code> whose a 
     * public method return value of this Enum is 
     * equal to <code>valor</code>.<br/>
     * Such method should be unique public, not final and static method 
     * declared in Enum.
     * In case of more than one method in match those conditions
     * its first one will be chosen.
     * 
     * @param enumType
     * @param value
     * @return 
     */
    public static <E extends Enum<E>> E from(Class<E> enumType, Object value) {
        String methodName = getMethodIdentifier(enumType);
        return from(enumType, value, methodName);
    }

    /**
     * Returns the <code>Enum</code> of type <code>enumType</code> whose  
     * public method <code>methodName</code> return is 
     * equal to <code>value</code>.<br/>
     *
     * @param enumType
     * @param value
     * @param methodName
     * @return
     */
    public static <E extends Enum<E>> E from(Class<E> enumType, Object value, String methodName) {
        EnumSet<E> enumSet = EnumSet.allOf(enumType);
        for (E en : enumSet) {
            try {
                String invoke = enumType.getMethod(methodName).invoke(en).toString();
                if (invoke.equals(value.toString())) {
                    return en;
                }
            } catch (Exception e) {
                return null;
            }
        }
        return null;
    }

    private static String getMethodIdentifier(Class<?> enumType) {
        Method[] methods = enumType.getDeclaredMethods();
        String name = null;
        for (Method method : methods) {
            int mod = method.getModifiers();
            if (Modifier.isPublic(mod) && !Modifier.isStatic(mod) && !Modifier.isFinal(mod)) {
                name = method.getName();
                break;
            }
        }
        return name;
    }
}

ตัวอย่าง:

public enum Foo {
    ONE("eins"), TWO("zwei"), THREE("drei");

    private String value;

    private Foo(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}

EnumUtil.from(Foo.class, "drei")ผลตอบแทนFoo.THREEเพราะมันจะใช้getValueเพื่อให้ตรงกับ "drei" ซึ่งเป็นสาธารณะที่ไม่ซ้ำกันไม่ใช่วิธีสุดท้ายและไม่คงที่ใน Foo ในกรณีฟูมีมากกว่าประชาชนไม่สิ้นสุดและไม่คงที่วิธีการเช่นgetTranslateที่ผลตอบแทน "drei" วิธีการอื่น ๆ EnumUtil.from(Foo.class, "drei", "getTranslate")ที่สามารถนำมาใช้:


1

โซลูชั่น Kotlin

valueOf<MyEnum>("value")สร้างส่วนขยายแล้วโทร หากประเภทไม่ถูกต้องคุณจะได้รับโมฆะและต้องจัดการ

inline fun <reified T : Enum<T>> valueOf(type: String): T? {
    return try {
        java.lang.Enum.valueOf(T::class.java, type)
    } catch (e: Exception) {
        null
    }
}

หรือคุณสามารถตั้งค่าเริ่มต้นการโทรvalueOf<MyEnum>("value", MyEnum.FALLBACK)และการหลีกเลี่ยงการตอบกลับที่ไม่เป็นผล คุณสามารถขยาย enum เฉพาะของคุณเพื่อให้ค่าเริ่มต้นเป็นแบบอัตโนมัติ

inline fun <reified T : Enum<T>> valueOf(type: String, default: T): T {
    return try {
        java.lang.Enum.valueOf(T::class.java, type)
    } catch (e: Exception) {
        default
    }
}

หรือถ้าคุณต้องการทั้งสองทำอย่างที่สอง:

inline fun <reified T : Enum<T>> valueOf(type: String, default: T): T = valueOf<T>(type) ?: default

คุณคิดว่าคำตอบของคุณจะมีบ้านที่ดีกว่าที่นี่หรือไม่? stackoverflow.com/questions/28548015/…
nabster

0

ฉันชอบที่จะใช้กระบวนการประเภทนี้เพื่อแยกคำสั่งเป็นสตริงในการแจกแจง ปกติแล้วฉันจะมีหนึ่งในการแจกแจงว่า "ไม่ทราบ" ดังนั้นจึงช่วยให้มีการส่งคืนเมื่อไม่พบผู้อื่น (แม้ในกรณีที่ไม่มีความรู้สึกพื้นฐาน) แทนที่จะเป็นโมฆะ (หมายถึงไม่มีค่า) ดังนั้นฉันใช้วิธีนี้

static <E extends Enum<E>> Enum getEnumValue(String what, Class<E> enumClass) {
    Enum<E> unknown=null;
    for (Enum<E> enumVal: enumClass.getEnumConstants()) {  
        if (what.compareToIgnoreCase(enumVal.name()) == 0) {
            return enumVal;
        }
        if (enumVal.name().compareToIgnoreCase("unknown") == 0) {
            unknown=enumVal;
        }
    }  
    return unknown;
}

-1

วิธีที่เร็วที่สุดในการรับชื่อ enum คือการสร้างแผนที่ของข้อความและค่า enum เมื่อเริ่มต้นแอปพลิเคชันและรับชื่อเรียกใช้ฟังก์ชัน Blah.getEnumName ():

public enum Blah {
    A("text1"),
    B("text2"),
    C("text3"),
    D("text4");

    private String text;
    private HashMap<String, String> map;
    Blah(String text) {
    this.text = text;
    }

    public String getText() {
      return this.text;
    }

    static{
      createMapOfTextAndName();
    }

    public static void createMapOfTextAndName() {
        map = new HashMap<String, String>();
        for (Blah b : Blah.values()) {
             map.put(b.getText(),b.name());
        }
    }
    public static String getEnumName(String text) {
        return map.get(text.toLowerCase());
    } 
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.