2010-03-10 34 views
31

Chúng tôi có một API REST nơi khách hàng có thể cung cấp các tham số đại diện cho các giá trị được xác định trên máy chủ trong Java Enums.Thực hành tốt nhất để tra cứu Java Enum

Vì vậy, chúng tôi có thể cung cấp lỗi mô tả, chúng tôi thêm phương thức lookup này vào mỗi Enum. Có vẻ như chúng tôi chỉ đang sao chép mã (xấu). Có cách nào tốt hơn không?

public enum MyEnum { 
    A, B, C, D; 

    public static MyEnum lookup(String id) { 
     try { 
      return MyEnum.valueOf(id); 
     } catch (IllegalArgumentException e) { 
      throw new RuntimeException("Invalid value for my enum blah blah: " + id); 
     } 
    } 
} 

Cập nhật: Các thông báo lỗi mặc định được cung cấp bởi valueOf(..) sẽ No enum const class a.b.c.MyEnum.BadValue. Tôi muốn cung cấp lỗi mô tả hơn từ API.

+2

Vâng, không có gì sai với IllegalArgumentException, mà đã là một RuntimeException. Bạn muốn cải thiện điều gì ở đây? – Riduidel

+0

Thêm thông điệp mô tả hơn so với thông báo được cung cấp khi chỉ sử dụng 'valueOf' (và' IllegalArgumentException') –

Trả lời

26

Có thể bạn có thể triển khai phương pháp tĩnh chung lookup.

Giống như rất

public class LookupUtil { 
    public static <E extends Enum<E>> E lookup(Class<E> e, String id) { 
     try {   
     E result = Enum.valueOf(e, id); 
     } catch (IllegalArgumentException e) { 
     // log error or something here 

     throw new RuntimeException(
      "Invalid value for enum " + e.getSimpleName() + ": " + id); 
     } 

     return result; 
    } 
} 

Sau đó, bạn có thể

public enum MyEnum { 
    static public MyEnum lookup(String id) { 
     return LookupUtil.lookup(MyEnum.class, id); 
    } 
} 

hoặc gọi một cách rõ ràng tiện ích tra cứu phương pháp lớp.

+0

Bạn không thể tạo một enum cơ sở để mở rộng enums con, như "Tất cả enums ngầm mở rộng java.lang.Enum. không hỗ trợ đa thừa kế, một enum không thể mở rộng bất cứ thứ gì khác ", vì vậy không có nơi nào để đặt mã này, trừ khi bạn muốn đặt nó trong một số loại lớp tiện ích tĩnh và gọi nó với tham số lớp enum. –

+0

Phương pháp tiện ích của bạn cung cấp lợi ích gì cho việc gọi Enum.valueOf? – sleske

+0

Hành động trước và sau và xử lý lỗi. Theo tôi hiểu nó là xử lý lỗi được sao chép và dán trên các loại enum khác nhau. –

3

Tại sao chúng ta phải viết mã 5 dòng đó?

public class EnumTest { 
public enum MyEnum { 
    A, B, C, D; 
} 

@Test 
public void test() throws Exception { 
    MyEnum.valueOf("A"); //gives you A 
    //this throws ILlegalargument without having to do any lookup 
    MyEnum.valueOf("RADD"); 
} 
} 
+0

Ý tưởng/yêu cầu là để ném lại ngoại lệ đặc biệt –

+0

Đúng vậy. Xem bài đăng được cập nhật để biết thêm một chút mô tả về lỗi "mặc định" so với lỗi mô tả hơn. –

+0

Trong trường hợp đó, câu trả lời từ @ "Mykola Golubyev" đã giải quyết được vấn đề của bạn là lớp tiện ích tra cứu tĩnh và bạn có thể ném bất kỳ thông báo/ngoại lệ nào bạn muốn trong khối catch? –

3

Nếu bạn muốn tra cứu được trường hợp nhạy cảm, bạn có thể lặp qua các giá trị làm cho nó thân thiện hơn một chút:

public enum MyEnum { 
    A, B, C, D; 

     public static MyEnum lookup(String id) { 
     boolean found = false; 
     for(MyEnum enum: values()){ 
      if(enum.toString().equalsIgnoreCase(id)) found = true; 
     } 
     if(!found) throw new RuntimeException("Invalid value for my enum: " +id); 
     } 
} 
+0

Đề xuất tốt, không thực sự là những gì tôi đang tìm kiếm, nhưng là một ý tưởng hay. Lưu ý trong 'tra cứu 'của bạn, bạn không thực sự trả về bất kỳ giá trị nào từ phương thức. –

+0

Điểm tốt. Nên là: if (enum.toString(). EqualsIgnoreCase (id)) trả về enum; Sau đó bạn có thể loại bỏ boolean và chỉ ném ngoại lệ nếu bạn vượt qua vòng lặp. – Adam

11

Hình như bạn có một thói quen xấu ở đây nhưng không phải là nơi bạn nghĩ.

Bắt một số IllegalArgumentException để tính lại một số RuntimeException khác với thông điệp rõ ràng hơn có thể giống như ý tưởng hay nhưng không phải. Bởi vì nó có nghĩa là bạn quan tâm đến các thông điệp trong trường hợp ngoại lệ của bạn.

Nếu bạn quan tâm đến thư trong trường hợp ngoại lệ, thì điều đó có nghĩa là người dùng của bạn bằng cách nào đó nhìn thấy ngoại lệ của bạn. Thật tệ.

Nếu bạn muốn cung cấp thông báo lỗi rõ ràng cho người dùng, bạn nên kiểm tra tính hợp lệ của giá trị enum khi phân tích cú pháp người dùng nhập và gửi thông báo lỗi thích hợp trong phản hồi nếu người dùng nhập sai.

Cái gì như:

// This code uses pure fantasy, you are warned! 
class MyApi 
{ 
    // Return the 24-hour from a 12-hour and AM/PM 

    void getHour24(Request request, Response response) 
    { 
     // validate user input 
     int nTime12 = 1; 
     try 
     { 
      nTime12 = Integer.parseInt(request.getParam("hour12")); 
      if(nTime12 <= 0 || nTime12 > 12) 
      { 
       throw new NumberFormatException(); 
      } 
     } 
     catch(NumberFormatException e) 
     { 
      response.setCode(400); // Bad request 
      response.setContent("time12 must be an integer between 1 and 12"); 
      return; 
     } 

     AMPM pm = null; 
     try 
     { 
      pm = AMPM.lookup(request.getParam("pm")); 
     } 
     catch(IllegalArgumentException e) 
     { 
      response.setCode(400); // Bad request 
      response.setContent("pm must be one of " + AMPM.values()); 
      return; 
     } 

     response.setCode(200); 
     switch(pm) 
     { 
      case AM: 
       response.setContent(nTime12); 
       break; 
      case PM: 
       response.setContent(nTime12 + 12); 
       break; 
     } 
     return; 
    } 
} 
+0

Tôi nghĩ rằng trong bản chất chúng tôi làm những gì bạn đang đề xuất. Chúng tôi ném ngoại lệ, sau đó một lớp cao hơn trên ngăn xếp bắt nó, sau đó trả về chỉ là thông báo ngoại lệ cho khách hàng thông qua REST API (trong một phản ứng lỗi được xác định) –

+5

Những gì tôi đề nghị là bạn không nên bắt ngoại lệ chung để trả lại tin nhắn, nhưng trả lại tin nhắn có nghĩa là để giải thích lý do tại sao ngoại lệ bị bắt. Có một phản ứng lỗi chung dựa trên ngoại lệ của bạn là sự rò rỉ của kiến ​​trúc bên trong cho máy khách và là một thực hành không tốt. Thông báo lỗi của API của bạn không nên phụ thuộc vào kiến ​​trúc báo cáo lỗi của việc triển khai (trong trường hợp này là các ngoại lệ Java). –

2

Các thông báo lỗi trong IllegalArgumentException là đủ đã mô tả.

Phương pháp của bạn làm cho ngoại lệ chung ra khỏi một trường hợp cụ thể với cùng một thông điệp được viết lại đơn giản. Một nhà phát triển sẽ thích loại ngoại lệ cụ thể và có thể xử lý trường hợp một cách thích hợp thay vì cố gắng xử lý RuntimeException.

Nếu mục đích là làm cho thông điệp thân thiện với người dùng hơn, thì các tham chiếu đến giá trị của enums không liên quan đến chúng. Hãy để mã giao diện người dùng xác định những gì sẽ được hiển thị cho người dùng và nhà phát triển UI sẽ tốt hơn với IllegalArgumentException.

+0

Thông báo lỗi "mặc định" sẽ là: 'Không có lớp enum const a.b.c.MyEnum.BadValue'. Tôi muốn trả về một thông báo lỗi có liên quan hơn từ REST API. Nhà phát triển tạo giao diện người dùng sẽ không thực sự biết phải làm gì với lỗi mặc định ngoài việc chỉ hiển thị nó - đó là những gì tôi muốn tránh. –

+2

Tôi không thấy thông điệp mẫu của bạn tốt hơn như thế nào. Không phù hợp cho người dùng vì vậy ngoại lệ phải được xử lý và thông báo phù hợp với người dùng được hiển thị. Điều này là khác nhau sau đó đặt thông điệp trong trường hợp ngoại lệ, mà nên được cho các nhà phát triển. – Robin

3

update: Như GreenTurtle nhận xét một cách chính xác, sau đây là sai


tôi sẽ chỉ cần viết

boolean result = Arrays.asList(FooEnum.values()).contains("Foo"); 

Đây có thể là ít performant hơn bắt một ngoại lệ thời gian chạy, nhưng làm cho mã sạch hơn . Bắt ngoại lệ như vậy luôn luôn là một ý tưởng tồi, vì nó dễ bị chẩn đoán sai. Điều gì sẽ xảy ra khi việc truy xuất giá trị so sánh chính nó gây ra một IllegalArgumentException? Điều này sau đó sẽ được xử lý như một giá trị không phù hợp cho điều tra viên.

+6

Mã này sẽ không bao giờ trả về true trừ khi bạn thực hiện trong lớp FooEnum của bạn một phương thức equals có thể xử lý các đối tượng String. Điều này xảy ra vì ArrayList được tạo ra chứa các đối tượng kiểu FooEnum và sẽ luôn trả về false nếu chứa được yêu cầu với một đối tượng String. – GreenTurtle

+0

@ GreenTurtle Có bạn rõ ràng là đúng, quên rằng Enum # bằng chỉ hoạt động với đối tượng so sánh. Đối với enums, bạn thậm chí không nên có thể thực hiện bằng của riêng bạn. Tôi vẫn sẽ cố gắng tránh bắt ngoại lệ thời gian chạy chung mặc dù và chỉ cần lặp qua nó, làm một chuỗi so sánh. – makrom

0

Chúng tôi làm tất cả các enums của chúng tôi như thế này khi nói đến Rest/Json vv Nó có lợi thế là lỗi là con người có thể đọc được và cũng cung cấp cho bạn danh sách giá trị được chấp nhận. Chúng tôi đang sử dụng một phương pháp tùy chỉnh MyEnum.fromString thay vì MyEnum.valueOf, hy vọng nó sẽ giúp.

public enum MyEnum { 

    A, B, C, D; 

    private static final Map<String, MyEnum> NAME_MAP = Stream.of(values()) 
      .collect(Collectors.toMap(MyEnum::toString, Function.identity())); 

    public static MyEnum fromString(final String name) { 
     MyEnum myEnum = NAME_MAP.get(name); 
     if (null == myEnum) { 
      throw new IllegalArgumentException(String.format("'%s' has no corresponding value. Accepted values: %s", name, Arrays.asList(values()))); 
     } 
     return myEnum; 
    } 
} 

nên ví dụ nếu bạn gọi

MyEnum value = MyEnum.fromString("X"); 

bạn sẽ nhận được một IllegalArgumentException với thông báo sau:

'X' không có giá trị tương ứng. Giá trị được chấp nhận: [A, B, C, D]

bạn có thể thay đổi IllegalArgumentException thành tùy chỉnh.

Các vấn đề liên quan