Giao diện đánh dấu trong Java?


134

Tôi đã được dạy rằng giao diện Marker trong Java là một giao diện trống và được sử dụng để báo hiệu cho trình biên dịch hoặc JVM rằng các đối tượng của lớp thực hiện giao diện này phải được xử lý theo một cách đặc biệt, như tuần tự hóa, nhân bản, v.v.

Nhưng gần đây tôi đã biết rằng nó thực sự không liên quan gì đến trình biên dịch hoặc JVM. Ví dụ, trong trường hợp Serializablecác phương pháp giao diện writeObject(Object)của ObjectOutputStreamlàm điều gì đó như instanceOf Serializableđể phát hiện xem các cụ lớp Serializable& ném NotSerializableExceptioncho phù hợp. Mọi thứ đều được xử lý trong mã và đây dường như là một mẫu thiết kế nên tôi nghĩ chúng ta có thể xác định các giao diện đánh dấu của riêng mình.

Bây giờ tôi nghi ngờ:

  1. Là định nghĩa của một giao diện đánh dấu được đề cập ở trên trong điểm 1 sai? Làm thế nào chúng ta có thể xác định một giao diện Marker sau đó?

  2. Và thay vì sử dụng instanceOftoán tử tại sao phương thức không thể giống như writeObject(Serializable)vậy để có kiểu kiểm tra thời gian biên dịch thay vì thời gian chạy?

  3. Làm thế nào là chú thích tốt hơn giao diện đánh dấu?


8
Serializablenhư một chú thích là vô nghĩa và @NonNullnhư một giao diện là vô nghĩa. Tôi sẽ nói: Chú thích là Markers + Metadata. BTW: Tiền thân của Annotations là XDoclet, sinh ra ở Javadoc, bị giết bởi Annotations.
Grim

Câu trả lời:


117
  1. Là định nghĩa của một giao diện đánh dấu được đề cập ở trên trong điểm 1 sai? - Điều này đúng trong các phần (1) giao diện đánh dấu phải trống và (2) thực hiện nó có nghĩa là ngụ ý một số đối xử đặc biệt của lớp thực hiện. Phần không chính xác là nó ngụ ý rằng JVM hoặc trình biên dịch sẽ đối xử với các đối tượng của lớp đó theo cách khác: bạn đúng khi quan sát rằng đó là mã của thư viện lớp Java coi các đối tượng này là có thể sao chép, tuần tự hóa, v.v. không có gì để làm với trình biên dịch hoặc JVM.
  2. thay vì sử dụng toán tử instanceOf tại sao phương thức không thể giống như writeObject(Serializable)vậy để có kiểm tra kiểu thời gian biên dịch - Điều này cho phép bạn tránh làm ô nhiễm mã của mình với tên của giao diện đánh dấu khi cần "đơn giản Object". Ví dụ, nếu bạn tạo một lớp cần được tuần tự hóa và có các thành viên đối tượng, bạn sẽ bị buộc phải thực hiện việc tạo hoặc tạo các đối tượng của mình Serializabletại thời gian biên dịch. Điều này là bất tiện, bởi vì giao diện không có bất kỳ chức năng.
  3. Làm thế nào chú thích tốt hơn giao diện đánh dấu? - Họ cho phép bạn đạt được cùng một mục đích là truyền tải siêu dữ liệu về lớp cho người tiêu dùng mà không tạo một loại riêng cho nó. Các chú thích cũng mạnh hơn, cho phép các lập trình viên truyền thông tin tinh vi hơn cho các lớp "tiêu thụ" nó.

14
Cách tôi luôn hiểu đó là Chú thích là một loại 'Giao diện đánh dấu 2.0': Nối tiếp tồn tại kể từ Java 1.1, Chú thích đã được thêm vào trong 1.5
blagae

và bởi vì writeObjectlà riêng tư, điều đó có nghĩa là ví dụ, một lớp không phải gọi triển khai siêu lớp
ratchet freak

15
Một điều cần lưu ý là các chú thích đã được thêm vào ngôn ngữ một vài năm sau khi thư viện chuẩn được thiết kế ban đầu. Nếu các chú thích đã có ngôn ngữ ngay từ đầu, thì nghi ngờ rằng serializable sẽ là một giao diện, nó có thể sẽ là một chú thích.
Theodore Norvell

Chà, trong trường hợp Cloneablekhông rõ liệu đó là thư viện hay xử lý JVM của cá thể bị thay đổi.
Holger

"[...] mà không tạo một loại riêng cho nó." - Tôi muốn nói rằng đây chính xác là những gì ngăn họ: Một giao diện đánh dấu không giới thiệu một loại, trong khi chú thích (loại) does't.
aioobe

22

Nó không phải là có thể thực thi Serializabletrên writeObjectvì trẻ em của lớp không serializable có thể serializable, nhưng trường hợp của họ có thể upcasted trở lại lớp cha mẹ. Kết quả là, giữ một tham chiếu đến một cái gì đó không tuần tự hóa (như Object) không có nghĩa là thể hiện được đề cập có thể thực sự được tuần tự hóa. Ví dụ trong

   Object x = "abc";
   if (x instanceof Serializable) {
   }

lớp cha ( Object) không được tuần tự hóa và sẽ được khởi tạo bằng cách sử dụng hàm tạo không tham số của nó. Giá trị được tham chiếu bởi x,, Stringlà tuần tự hóa và câu lệnh có điều kiện sẽ chạy.


6

Giao diện đánh dấu trong Java là một giao diện không có trường hoặc phương thức. Nói một cách đơn giản hơn, một giao diện trống trong Java được gọi là giao diện đánh dấu. Ví dụ về các giao diện marker là Serializable, CloneableRemotegiao diện. Chúng được sử dụng để chỉ ra một số thông tin cho trình biên dịch hoặc JVM. Vì vậy, nếu JVM thấy rằng một lớp là Serializable, nó có thể thực hiện một số thao tác đặc biệt trên nó. Tương tự, nếu JVM thấy một số lớp đang triển khai Cloneable, nó có thể thực hiện một số hoạt động để hỗ trợ nhân bản. Điều này cũng đúng với RMI và Remotegiao diện. Vì vậy, trong ngắn hạn, một giao diện đánh dấu chỉ ra một tín hiệu hoặc một lệnh cho trình biên dịch hoặc JVM.

Ở trên bắt đầu như một bản sao của một bài đăng blog nhưng đã được chỉnh sửa nhẹ cho ngữ pháp.


6
Bạn có thể sao chép nhưng cũng vui lòng đề cập đến nguồn: javarevisited.blogspot.com/2012/01/ . Ngoài ra, nó sẽ rất tốt nếu bạn không sao chép dán lỗi chính tả. :)
Saurabh Patil

5

a / Một giao diện đánh dấu như tên của nó gợi ý chỉ tồn tại để thông báo cho bất cứ điều gì biết về nó mà một lớp khai báo điều gì đó. Bất cứ thứ gì cũng có thể là các lớp JDK cho Serializablegiao diện hoặc bất kỳ lớp nào bạn viết yoursel cho một tùy chỉnh.

b / Nếu là giao diện đánh dấu, nó không nên ngụ ý sự tồn tại của bất kỳ phương thức nào - tốt hơn là đưa phương thức ngụ ý vào giao diện. Nhưng bạn có thể quyết định thiết kế nó theo ý muốn nếu bạn biết tại sao bạn cần nó

c / Có rất ít sự khác biệt giữa giao diện trống và chú thích không sử dụng giá trị hoặc tham số. Nhưng sự khác biệt là có: một chú thích có thể khai báo danh sách các khóa / giá trị sẽ có thể truy cập được trong thời gian chạy.


5

Tôi đã thực hiện một minh chứng đơn giản để giải quyết nghi ngờ số 1 và 2:

Chúng tôi sẽ có giao diện Movable đó sẽ được thực hiện bởi MobilePhone.javalớp và lớp một nhiều LandlinePhone.javamà làm không thực hiện giao diện Movable

Giao diện đánh dấu của chúng tôi:

package com;

public interface Movable {

}

LandLinePhone.javaMobilePhone.java

 package com;

 class LandLinePhone {
    // more code here
 }
 class MobilePhone implements Movable {
    // more code here
 }

Lớp ngoại lệ tùy chỉnh của chúng tôi: gói com;

public class NotMovableException extends Exception {

private static final long serialVersionUID = 1L;

    @Override
    public String getMessage() {
        return "this object is not movable";
    }
    // more code here
    }

Lớp kiểm tra của chúng tôi: TestMArkerInterface.java

package com;

public class TestMarkerInterface {

public static void main(String[] args) throws NotMovableException {
    MobilePhone mobilePhone = new MobilePhone();
    LandLinePhone landLinePhone = new LandLinePhone();

    TestMarkerInterface.goTravel(mobilePhone);
    TestMarkerInterface.goTravel(landLinePhone);
}

public static void goTravel(Object o) throws NotMovableException {
    if (!(o instanceof Movable)) {
        System.out.println("you cannot use :" + o.getClass().getName() + "   while travelling");
        throw new NotMovableException();
    }

    System.out.println("you can use :" + o.getClass().getName() + "   while travelling");
}}

Bây giờ khi chúng ta thực hiện lớp chính:

you can use :com.MobilePhone while travelling
you cannot use :com.LandLinePhone while travelling
Exception in thread "main" com.NotMovableException: this object is not movable
    at com.TestMarkerInterface.goTravel(TestMarkerInterface.java:22)
    at com.TestMarkerInterface.main(TestMarkerInterface.java:14)

Vì vậy, lớp nào thực hiện giao diện đánh dấu Movablesẽ vượt qua thông báo lỗi kiểm tra khác sẽ được hiển thị.

Đây là cách instanceOfkiểm tra toán tử được thực hiện cho Nối tiếp , Clonizable , v.v.


3

a. Tôi luôn thấy chúng là một mẫu thiết kế và không có gì đặc biệt JVM Tôi đã sử dụng mẫu đó trong một số tình huống.

c. Tôi tin rằng sử dụng Chú thích để đánh dấu một cái gì đó là một giải pháp tốt hơn sau đó sử dụng các giao diện đánh dấu. Đơn giản là vì Giao diện nằm ở vị trí đầu tiên nhằm xác định các giao diện chung của Loại / Lớp. Họ là một phần của hierachy lớp.

Các chú thích nhằm mục đích cung cấp Meta-Information cho Code và tôi nghĩ rằng điểm đánh dấu là thông tin meta. Vì vậy, họ là chính xác cho trường hợp sử dụng.


3
  1. Nó không có gì để làm (nhất thiết) với JVM và trình biên dịch, nó có liên quan đến bất kỳ mã nào quan tâm và đang kiểm tra giao diện đánh dấu đã cho.

  2. Đó là một quyết định thiết kế và nó được thực hiện vì một lý do tốt. Xem câu trả lời từ Audrius Meškauskas.

  3. Đối với chủ đề cụ thể này, tôi không nghĩ đó là vấn đề tốt hơn hay tồi tệ hơn. Giao diện đánh dấu đang làm những gì nó được cho là tốt.


Bạn có thể thêm một liên kết đến "câu trả lời từ Audrius Meškauskas" không? Tôi không thấy bất cứ điều gì trên trang này với tên đó.
Sarah Messer

2

Mục đích chính của giao diện đánh dấu là tạo ra các loại đặc biệt trong đó bản thân các loại không có hành vi của riêng chúng.

public interface MarkerEntity {

}

public boolean save(Object object) throws InvalidEntityFoundException {
   if(!(object instanceof MarkerEntity)) {
       throw new InvalidEntityFoundException("Invalid Entity Found, can't be  saved);
   } 
   return db.save(object);
}

Ở đây phương thức lưu đảm bảo rằng chỉ các đối tượng của các lớp thực hiện giao diện MarkerEntity được lưu, đối với các loại khác UnlimitedEntityFoundException được ném. Vì vậy, ở đây giao diện đánh dấu MarkerEntity đang xác định một loại thêm hành vi đặc biệt cho các lớp thực hiện nó.

Mặc dù các chú thích cũng có thể được sử dụng ngay bây giờ để đánh dấu các lớp cho một số phương pháp điều trị đặc biệt nhưng chú thích đánh dấu là thay thế cho mẫu đặt tên không dành cho giao diện Marker.

Nhưng các chú thích đánh dấu không thể thay thế hoàn toàn các giao diện đánh dấu bởi vì; giao diện đánh dấu được sử dụng để xác định loại (như đã giải thích ở trên) trong đó như các chú thích đánh dấu không.

Nguồn cho nhận xét giao diện đánh dấu


1
+1 để chỉ ra rằng nó thực sự được sử dụng như một cái móc "an toàn" đơn thuần mà một lập trình viên phải nói rõ ràng rằng nó có thể được lưu để làm ví dụ cho cơ sở dữ liệu.
Ludvig W

1

Tôi sẽ tranh luận trước tiên rằng Nối tiếp và Clonizable là những ví dụ xấu về giao diện đánh dấu. Chắc chắn, chúng là giao diện với các phương thức, nhưng chúng ngụ ý các phương thức, chẳng hạn như writeObject(ObjectOutputStream). (Trình biên dịch sẽ tạo một writeObject(ObjectOutputStream)phương thức cho bạn nếu bạn không ghi đè lên nó và tất cả các đối tượng đã có clone(), nhưng trình biên dịch sẽ lại tạo một phương thức thựcclone() phương thức cho bạn nhưng hãy cẩn thận. ví dụ thiết kế tốt.)

Giao diện đánh dấu thường được sử dụng cho một trong hai mục đích:

1) Là một lối tắt để tránh một loại quá dài, có thể xảy ra với rất nhiều thuốc generic. Ví dụ: giả sử bạn có chữ ký phương thức này:

public void doSomething(Foobar<String, Map<String, SomethingElse<Integer, Long>>>) { ... }

Điều đó lộn xộn và khó chịu khi gõ, và quan trọng hơn, khó hiểu. Thay vào đó hãy xem xét điều này:

public interface Widget extends Foobar<String, Map<String, SomethingElse<Integer, Long>>> { }

Sau đó, phương pháp của bạn trông như thế này:

public void doSomething(Widget widget) { ... }

Không chỉ rõ ràng hơn, mà giờ đây bạn có thể Javadoc giao diện Widget và việc tìm kiếm tất cả các lần xuất hiện trong mã Widget của bạn cũng dễ dàng hơn.

2) Các giao diện đánh dấu cũng có thể được sử dụng như một cách xung quanh việc thiếu các loại giao lộ của Java. Với giao diện đánh dấu, bạn có thể yêu cầu một số loại phải có hai loại khác nhau, chẳng hạn như trong chữ ký phương thức. Giả sử bạn có một số Widget giao diện trong ứng dụng của mình, như chúng tôi đã mô tả ở trên. Nếu bạn có một phương pháp yêu cầu một Widget cũng tình cờ cho phép bạn lặp lại nó (nó có thể, nhưng làm việc với tôi ở đây), giải pháp tốt duy nhất của bạn là tạo giao diện đánh dấu mở rộng cả hai giao diện:

public interface IterableWidget extends Iterable<String>, Widget { }

Và trong mã của bạn:

public void doSomething(IterableWidget widget) {
    for (String s : widget) { ... }
}

1
Trình biên dịch không tạo ra bất kỳ phương thức nào nếu lớp của bạn thực hiện Serializablehoặc Cloneable. Bạn có thể xác minh nó bằng cách kiểm tra các tập tin lớp của bạn. Hơn nữa, việc tạo các giao diện phím tắt Tiếng Việt không phải là giao diện đánh dấu. Và đó là một thực tế mã hóa xấu thực sự vì nó sẽ yêu cầu tạo các lớp triển khai bổ sung để hoàn thành các giao diện bổ sung. Nhân tiện, Java các loại giao nhau trong một thập kỷ nay. Tìm hiểu về Generics từ
Holger

Để nhân bản, bạn đúng. Nó không thay đổi những gì Object.clone () làm. Nó vẫn còn kinh khủng. Xem liên kết Josh Bloch Các giao diện Shortcut không nhất thiết phải là một mẫu thiết kế tốt, vì bạn tự ý hạn chế những gì bạn có thể gửi cho một phương thức. Đồng thời, tôi cảm thấy rằng viết rõ ràng hơn, nếu mã hạn chế hơn một chút, thường là một sự đánh đổi hợp lý. Đối với các loại giao lộ, điều đó chỉ áp dụng cho thuốc generic, và không thể khử được, và do đó vô dụng trong nhiều trường hợp. Hãy thử có một biến đối tượng là cả serializable và, tốt, bất cứ điều gì khác.
MikeyB

0

Nếu một giao diện không chứa bất kỳ phương thức nào và bằng cách thực hiện giao diện đó, nếu đối tượng của chúng ta sẽ có một số khả năng loại giao diện như vậy được gọi là giao diện đánh dấu.

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.