Ghi đè valueof () và toString () trong Java enum


122

Các giá trị trong của tôi enumlà những từ cần có khoảng trắng trong chúng, nhưng enums không thể có khoảng trắng trong các giá trị của chúng nên tất cả đều được gộp lại. Tôi muốn ghi đè toString()để thêm các khoảng trắng này vào nơi tôi yêu cầu.

Tôi cũng muốn enum cung cấp đúng enum khi tôi sử dụng valueOf()trên cùng một chuỗi mà tôi đã thêm dấu cách vào.

Ví dụ:

public enum RandomEnum
{
     StartHere,
     StopHere
}

Gọi toString()vào chuỗi RandomEnumcó giá trị StartHeretrả về "Start Here". Gọi valueof()trên cùng một chuỗi ( "Start Here") trả về giá trị enum StartHere.

Tôi có thể làm cái này như thế nào?


2
Thêm thẻ "Java" để nhận thêm nhận xét / câu trả lời.
Java42

Câu trả lời:


197

Bạn có thể thử mã này. Vì bạn không thể ghi đè valueOfphương thức, bạn phải xác định một phương thức tùy chỉnh ( getEnumtrong mã mẫu bên dưới) trả về giá trị mà bạn cần và thay đổi ứng dụng của bạn để sử dụng phương pháp này.

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 có thể được rút ngắn nếu bạn làm như sau: v.getValue (). equals (value); có thể bỏ qua kiểm tra null.
rtcarlson

Bạn cũng có thể lười biếng xây dựng bản đồ và sử dụng lại nó, nhưng hãy cẩn thận với các vấn đề phân luồng trong khi xây dựng nó.
Maarten Bodewes

11
không làm việc trong một tình huống mà bạn không thể thay đổi sự kêu gọi của valueOf () phương pháp để getValue () ví dụ như khi bạn xác định các lĩnh vực nhất định qua chú thích Hibernate để ánh xạ giá trị enum
mmierins

Cho đến khi Enums được sửa trong Java, @Bat có một giải pháp phù hợp hơn và thân thiện với OO. tên () không nên là cuối cùng.
Andrew T Finnell,

thay vì cho bạn có thể sử dụng valueOf (RandomEnum.class, value);
Wojtek

22

Hãy thử điều này, nhưng tôi không chắc rằng nó sẽ hoạt động ở mọi nơi :)

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
Tại sao tôi là người duy nhất mà loại thứ đó trông có vẻ xấu xa? Ý tôi là nó trông rất tuyệt nhưng ai đó không có ngữ cảnh đọc mã đó rất có thể sẽ giống như "WTF?".
Nicolas Guillaume

11
@NicolasGuillaume Đó là loại mã, nếu được triển khai, sẽ rất cần một nhận xét giải thích tại sao nó ở đó và vấn đề mà lập trình viên đang cố gắng giải quyết. :-)
Ti Strga

9
Đừng bắt ngoại lệ chung chung, vì điều này có thể là OutOfMemory hoặc bất cứ điều gì
crusy

2
Đây là một ý tưởng khủng khiếp. Không ít trong số đó là khả năng xảy ra lỗi im lặng mà không có dấu hiệu hoặc phục hồi. Ngoài ra bây giờ giá trị "tên" và giá trị biểu tượng trong mã (ví dụ: A, B) là khác nhau. +2 vì thông minh. -200 vì thông minh khủng khiếp. Không có cách nào tôi sẽ chấp nhận điều này một đánh giá mã.
pedorro

1
@pedorro Điều này có vẻ không quá khủng khiếp như bạn đang làm. Bất kỳ ai hiểu rằng mọi thứ mà ủy ban Java làm đều không nên được thực hiện vì Phúc âm sẽ thông qua điều này trong Đánh giá mã. Thật là tệ hơn khi phải viết lại và sau đó duy trì tất cả các phương thức tiện ích Enum vì không thể ghi đè tên (). Thay vì bắt ngoại lệ và nuốt nó, một RuntimeException nên được ném và sau đó điều này là tốt.
Andrew T Finnell,

14

Bạn có thể sử dụng một Bản đồ tĩnh trong enum của mình để ánh xạ Chuỗi thành hằng số enum. Sử dụng nó trong một phương thức tĩnh 'getEnum'. Điều này bỏ qua sự cần thiết phải lặp qua các enum mỗi khi bạn muốn lấy một từ giá trị Chuỗi của nó.

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

Chỉ cần đảm bảo rằng quá trình khởi tạo tĩnh của bản đồ xảy ra bên dưới phần khai báo các hằng số enum.

BTW - loại 'ImmutableMap' đó là từ API ổi của Google và tôi chắc chắn khuyên bạn nên sử dụng nó trong những trường hợp như thế này.


CHỈNH SỬA - Theo nhận xét:

  1. Giải pháp này giả định rằng mỗi giá trị chuỗi được gán là duy nhất và không rỗng. Cho rằng người tạo enum có thể kiểm soát điều này và chuỗi tương ứng với giá trị enum duy nhất & không null, điều này có vẻ như là một hạn chế an toàn.
  2. Tôi đã thêm phương thức 'toSTring ()' như được yêu cầu trong câu hỏi

1
Tôi đã nhận được một vài phiếu phản đối cho câu trả lời này - có ai muốn tìm hiểu thêm tại sao câu trả lời này lại tệ không? Tôi chỉ tò mò những gì mọi người đang nghĩ.
pedorro

Điều này sẽ không cho kịch bản có nhiều hằng enum với 'giá trị' cùng như RestartHere("replay"), ReplayHere("replay")hoặc không có 'giá trị' ở tất cả như PauseHere, WaitHere(ví dụ: enum có một cái gì đó constructor mặc định nhưprivate RandomEnum() { this.strVal = null; }
sactiw

1
Bạn nên sử dụng ImmutableMap.Builder thay vì tạo MutableMap để xây dựng + ImmutableMap :: copyOf.
Bryan Correll

Điều này trông thú vị nhưng nếu enum chỉ có một vài giá trị khác nhau thì sẽ không mất nhiều thời gian để lặp lại chúng. Nó có lẽ chỉ đáng giá nếu enum có nhiều giá trị.
Ben Thurley

13

Làm thế nào về việc triển khai Java 8? (null có thể được thay thế bằng Enum mặc định của bạn)

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

Hoặc bạn có thể sử dụng:

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

2

Tôi không nghĩ rằng bạn sẽ nhận được valueOf ("Bắt đầu tại đây") để hoạt động. Nhưng về khoảng cách ... hãy thử cách sau ...

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

Sau đây là một thay thế chung tốt đẹp cho 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);
}

là cách sử dụng compareTo()thanh lịch hơn so equals()với chuỗi?
Betlista

Thanh lịch không phải là một vấn đề - tôi đồng ý rằng điều đó .equals()sẽ hoạt động tốt. Có thể sử dụng ==nếu bạn thích. (Mặc dù lý do chính đáng là bạn không nên thay thế enum bằng một lớp.)
ngoại lệ

3
@exception KHÔNG BAO GIỜ SỬ DỤNG == để so sánh chuỗi vì nó sẽ thực hiện kiểm tra bình đẳng đối tượng trong khi bạn cần kiểm tra bình đẳng nội dung và sử dụng phương thức equals () của lớp String.
sactiw

2

Bạn vẫn có một tùy chọn để triển khai trong enum của mình:

public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name){...}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.