Java enum - tại sao sử dụng toString thay vì tên


171

Nếu bạn nhìn vào enum api ở phương thức name()thì nó nói rằng:

Trả về tên của hằng số enum này, chính xác như được khai báo trong khai báo enum của nó. Hầu hết các lập trình viên nên sử dụng phương thức toString theo sở thích này, vì phương thức toString có thể trả về một tên thân thiện hơn với người dùng. Phương pháp này được thiết kế chủ yếu để sử dụng trong các tình huống chuyên biệt trong đó tính chính xác phụ thuộc vào việc lấy tên chính xác, sẽ không thay đổi từ phát hành đến phát hành.

Tại sao tốt hơn để sử dụng toString()? Ý tôi là toString có thể bị ghi đè khi tên () đã là cuối cùng. Vì vậy, nếu bạn sử dụng toString và ai đó ghi đè lên nó để trả về giá trị được mã hóa cứng, toàn bộ ứng dụng của bạn sẽ bị hỏng ... Ngoài ra nếu bạn tìm trong các nguồn, phương thức toString () trả về chính xác và chỉ tên. Đó là một thứ tương tự.


4
Bạn có thể ghi đè toString()trong enum của mình, nhưng không ai khác có thể mở rộng và ghi đè lên nó. Bạn không thể mở rộng enums.
Erick G. Hagstrom

Câu trả lời:


198

Nó thực sự phụ thuộc vào những gì bạn muốn làm với giá trị được trả về:

  • Nếu bạn cần lấy tên chính xác được sử dụng để khai báo hằng số enum, bạn nên sử dụng name()toStringcó thể đã bị ghi đè
  • Nếu bạn muốn in hằng số enum theo cách thân thiện với người dùng, bạn nên sử dụng toStringcái có thể đã bị ghi đè (hoặc không!).

Khi tôi cảm thấy rằng nó có thể gây nhầm lẫn, tôi cung cấp một getXXXphương pháp cụ thể hơn , ví dụ:

public enum Fields {
    LAST_NAME("Last Name"), FIRST_NAME("First Name");

    private final String fieldDescription;

    private Fields(String value) {
        fieldDescription = value;
    }

    public String getFieldDescription() {
        return fieldDescription;
    }
}

1
Điều này được chấp nhận. Mặc dù nếu họ để enum có một lớp cơ sở thì mọi thứ sẽ dễ dàng hơn.
ralphgabb

55

Sử dụng name()khi bạn muốn so sánh hoặc sử dụng giá trị mã hóa cứng cho một số sử dụng nội bộ trong mã của bạn.

Sử dụng toString()khi bạn muốn trình bày thông tin cho người dùng (bao gồm cả nhà phát triển đang xem nhật ký). Không bao giờ dựa vào mã của bạn để toString()đưa ra một giá trị cụ thể. Không bao giờ kiểm tra nó đối với một chuỗi cụ thể. Nếu mã của bạn bị hỏng khi ai đó thay đổi chính xác toString()thì nó đã bị hỏng.

Từ javadoc (nhấn mạnh của tôi):

Trả về một chuỗi đại diện của các đối tượng. Nói chung, phương thức toString trả về một chuỗi "đại diện bằng văn bản" đối tượng này. Kết quả phải là một đại diện ngắn gọn nhưng đầy đủ thông tin để người đó dễ đọc . Chúng tôi đề nghị rằng tất cả các lớp con ghi đè phương thức này.


23

name()là một phương pháp "tích hợp" của enum. Nó là cuối cùng và bạn không thể thay đổi việc thực hiện nó. Nó trả về tên của hằng số enum như được viết, ví dụ trong chữ in hoa, không có dấu cách, v.v.

So sánh MOBILE_PHONE_NUMBERMobile phone number. Phiên bản nào dễ đọc hơn? Tôi tin cái thứ hai. Đây là sự khác biệt: name()luôn luôn trả về MOBILE_PHONE_NUMBER, toString()có thể được ghi đè để trả về Mobile phone number.


16
Điều này LAF không đúng!! toString () Mobile phone numberchỉ trả về nếu bạn ghi đè lên nó để trả về giá trị đó. Nếu không nó sẽ trở lạiMOBILE_PHONE_NUMBER
spauny

2
@spayny, rõ ràng là bạn phải ghi đè toString(). Cái bắt là name()không thể bị ghi đè vì nó là cuối cùng.
AlexR

13
@AlexR bạn có thể cần kết hợp nhận xét trên của mình vào câu trả lời để làm cho nó chính xác 100%
artlyContrived

5
Tôi đồng ý với @artfullyContrived vì điều đó không rõ ràng đối với tôi cho đến khi tôi đọc bình luận của bạn.
martinatime

13

Trong khi hầu hết mọi người mù quáng làm theo lời khuyên của javadoc, có những tình huống rất cụ thể mà bạn muốn thực sự tránh toString (). Ví dụ, tôi đang sử dụng enums trong mã Java của mình, nhưng chúng cần được tuần tự hóa thành cơ sở dữ liệu và quay lại. Nếu tôi đã sử dụng toString () thì về mặt kỹ thuật tôi sẽ phải chịu các hành vi bị ghi đè như những người khác đã chỉ ra.

Ngoài ra, người ta cũng có thể hủy tuần tự hóa từ cơ sở dữ liệu, ví dụ, điều này sẽ luôn hoạt động trong Java:

MyEnum taco = MyEnum.valueOf(MyEnum.TACO.name());

Trong khi điều này không được đảm bảo:

MyEnum taco = MyEnum.valueOf(MyEnum.TACO.toString());

Nhân tiện, tôi thấy thật kỳ quặc khi Javadoc nói rõ ràng "hầu hết các lập trình viên nên". Tôi tìm thấy rất ít trường hợp sử dụng trong toString of enum, nếu mọi người đang sử dụng tên đó cho "tên thân thiện" thì đó rõ ràng là trường hợp sử dụng kém vì họ nên sử dụng thứ gì đó tương thích hơn với i18n, trong hầu hết các trường hợp, sử dụng phương thức name ().


2
Cảm ơn đã trả lời. Đã gần 5 năm kể từ khi tôi hỏi câu hỏi này và tôi vẫn chưa sử dụng phương thức toString () cho một enum! 99% số lần tôi sử dụng enum chính xác cho những gì bạn mô tả: tuần tự hóa và giải mã tên enum đến / từ cơ sở dữ liệu.
spauny 17/03/2017

5

Một ví dụ thực tế khi name () và toString () có ý nghĩa khác nhau là một mẫu trong đó enum có giá trị đơn được sử dụng để định nghĩa một singleton. Thoạt nhìn có vẻ đáng ngạc nhiên nhưng có rất nhiều ý nghĩa:

enum SingletonComponent {
    INSTANCE(/*..configuration...*/);

    /* ...behavior... */

    @Override
    String toString() {
      return "SingletonComponent"; // better than default "INSTANCE"
    }
}

Trong trường hợp như vậy:

SingletonComponent myComponent = SingletonComponent.INSTANCE;
assertThat(myComponent.name()).isEqualTo("INSTANCE"); // blah
assertThat(myComponent.toString()).isEqualTo("SingletonComponent"); // better

Vâng, bạn nói đúng ... một ví dụ điển hình là Vị
tiên tri

4

name () nghĩa đen là tên văn bản trong mã java của enum. Điều đó có nghĩa là nó bị giới hạn ở các chuỗi thực sự có thể xuất hiện trong mã java của bạn, nhưng không phải tất cả các chuỗi mong muốn đều có thể biểu thị được trong mã. Ví dụ, bạn có thể cần một chuỗi bắt đầu bằng một số. Tên () sẽ không bao giờ có thể có được chuỗi đó cho bạn.


-1

Bạn cũng có thể sử dụng một cái gì đó như mã dưới đây. Tôi đã sử dụng lombok để tránh viết một số mã soạn sẵn cho getters và constructor.

@AllArgsConstructor
@Getter
public enum RetroDeviceStatus {
    DELIVERED(0,"Delivered"),
    ACCEPTED(1, "Accepted"),
    REJECTED(2, "Rejected"),
    REPAIRED(3, "Repaired");

    private final Integer value;
    private final String stringValue;

    @Override
    public String toString() {
        return this.stringValue;
    }
}
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.