2010-07-27 42 views
34

Trường hợp sử dụng hợp lệ để triển khai chú thích là gì?Các trường hợp sử dụng để thực hiện chú thích

Khi thiết kế chủ yếu các hệ thống định cấu hình dựa trên chú thích, đôi khi tôi cần phải tạo các lớp triển khai chú thích để tạo mã hoặc lập trình cấu hình.

Cách thay thế liên quan đến việc phản chiếu dữ liệu chứa trong chú thích vào DTO, điều này có vẻ giống như chi phí.

Dưới đây là một ví dụ:

public enum IDType { 
    LOCAL, 
    URI, 
    RESOURCE; 
} 

@Documented 
@Target({ METHOD, FIELD }) 
@Retention(RetentionPolicy.RUNTIME) 
@Inherited 
public @interface Id { 
    /** 
    * @return 
    */ 
    IDType value() default IDType.LOCAL; 
} 

với việc thực hiện

public class IdImpl implements Id{ 

    private final IDType idType; 

    public IdImpl(IDType idType){ 
     this.idType = idType; 
    } 

    @Override 
    public IDType value() { 
     return idType; 
    } 

    @Override 
    public Class<? extends Annotation> annotationType() { 
     return Id.class; 
    } 

} 

tôi nhận được cảnh báo trình biên dịch cho điều này, nhưng nó có vẻ là một công cụ có giá trị cho nhiều trường hợp sử dụng.

Cảnh báo ví dụ ở trên là

Loại chú thích Id không nên sử dụng như một superinterface cho IdImpl

được sửa đổi:

Tôi chỉ tìm thấy ví dụ này từ Guice :

bind(CreditCardProcessor.class) 
    .annotatedWith(Names.named("Checkout")) 
    .to(CheckoutCreditCardProcessor.class); 

Xem trang này Javadoc from Names.

Có ai biết một số thông tin tại sao hạn chế này tồn tại hoặc có một số trường hợp sử dụng khác trong đầu không?

+2

Bạn nhận được cảnh báo nào? – djna

+2

@djina: Vâng, một xu cho mỗi lần bạn phải nói điều đó và bạn sẽ giàu có. Nó không bao giờ hết ngạc nhiên tôi. – musiKk

+0

Có lẽ nó chỉ là tôi nhưng nó có vẻ liên quan chặt chẽ đến câu hỏi này: http: // stackoverflow.com/questions/1624084/tại sao-là-không-thể-để-mở rộng-chú thích-trong-java –

Trả lời

19

Tôi chưa bao giờ sử dụng nó trong thực tế nhưng những gì bạn nhận được là bạn có thể sử dụng các lớp để thay thế cho chú thích của mình.

Hãy tạo một ví dụ nhân tạo. Giả sử chúng ta có trình tạo tài liệu. Nó đọc chú thích @Docu từ các lớp nhất định và in thuộc tính description. Như thế này:

import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 
import java.util.ArrayList; 
import java.util.List; 

public class DokuGenerator { 

    public static void main(String[] args) throws Exception { 
     new DokuGenerator(StaticClass.class, StaticClass2.class); 
    } 

    public DokuGenerator(Class<?>... classesToDokument) throws Exception { 
     List<Docu> documentAnnotations = getDocumentAnnotations(classesToDokument); 
     printDocumentation(documentAnnotations); 
    } 

    private List<Docu> getDocumentAnnotations(Class<?>... classesToDokument) 
      throws Exception { 
     List<Docu> result = new ArrayList<Docu>(); 
     for (Class<?> c : classesToDokument) 
      if (c.isAnnotationPresent(Docu.class)) 
       result.add(c.getAnnotation(Docu.class)); 
     return result; 
    } 

    private void printDocumentation(List<Docu> toDocument) { 
     for (Docu m : toDocument) 
      System.out.println(m.description()); 
    } 

} 

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
@interface Docu { 
    String description(); 
} 

@Docu(description = "This is a static class!") 
class StaticClass { 
} 

@Docu(description = "This is another static class!") 
class StaticClass2 { 
} 

Prints:

This is a static class! 
This is another static class! 

gì bây giờ chúng tôi muốn đạt được là, có một lớp không chỉ có thể được chú thích staticly, nhưng có thể thêm thông tin thời gian chạy để tài liệu. Chúng tôi khá vui khi sử dụng chú thích @Docu hầu hết thời gian, nhưng có những trường hợp đặc biệt, chúng tôi muốn sự tài liệu đặc biệt. Chúng tôi có thể muốn thêm tài liệu hiệu suất cho một số phương pháp. Chúng ta có thể làm điều này bằng cách cho phép một lớp thực hiện chú thích. Máy phát điện kiểm tra đầu tiên cho chú thích và nếu không có chú thích, nó sẽ kiểm tra xem lớp có thực hiện chú thích hay không. Nếu có, nó sẽ thêm lớp vào danh sách chú thích.

Như thế này (chỉ có hai dòng bổ sung mã trong các máy phát điện):

import java.lang.annotation.Annotation; 
import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 

public class DokuGenerator { 

    public static void main(String[] args) throws Exception { 
     new DokuGenerator(StaticClass.class, StaticClass2.class, 
       DynamicClass.class); 
    } 

    public DokuGenerator(Class<?>... classesToDokument) throws Exception { 
     List<Docu> documentAnnotations = getDocumentAnnotations(classesToDokument); 
     printDocumentation(documentAnnotations); 
    } 

    private List<Docu> getDocumentAnnotations(Class<?>... classesToDokument) 
      throws Exception { 
     List<Docu> result = new ArrayList<Docu>(); 
     for (Class<?> c : classesToDokument) 
      if (c.isAnnotationPresent(Docu.class)) 
       result.add(c.getAnnotation(Docu.class)); 
      else if (Arrays.asList(c.getInterfaces()).contains(Docu.class)) 
       result.add((Docu) c.newInstance()); 
     return result; 
    } 

    private void printDocumentation(List<Docu> toDocument) { 
     for (Docu m : toDocument) 
      System.out.println(m.description()); 
    } 

} 

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
@interface Docu { 
    String description(); 
} 

@Docu(description = "This is a static class!") 
class StaticClass { 
} 

@Docu(description = "This is another static class!") 
class StaticClass2 { 
} 

class DynamicClass implements Docu { 

    public DynamicClass() { 
     try { 
      Thread.sleep((long) (Math.random() * 100)); 
     } catch (InterruptedException e) { 
      // ignore exception to make debugging a little harder 
     } 
    } 

    @Override 
    public String description() { 
     long millis = System.currentTimeMillis(); 
     new DynamicClass(); 
     millis = System.currentTimeMillis() - millis; 
     return "This is a dynamic class. I run on " 
       + System.getProperty("os.name") 
       + ". The construction of an instance of this class run for " 
       + millis + " milliseconds."; 
    } 

    @Override 
    public Class<? extends Annotation> annotationType() { 
     return Docu.class; 
    } 

} 

Output là:

This is a static class! 
This is another static class! 
This is a dynamic class. I run on Windows XP. The construction of an instance of this class run for 47 milliseconds. 

Bạn havn't để thay đổi bộ tạo mã để nhiều vì bạn có thể sử dụng lớp thay thế chú thích.

Ví dụ khác có thể là khuôn khổ sử dụng chú thích hoặc XML làm cấu hình. Bạn có thể có một bộ xử lý hoạt động trên chú thích. Nếu bạn sử dụng XML làm cấu hình, bạn có thể tạo ra các cá thể của các lớp thực hiện các chú thích và bộ vi xử lý của bạn hoạt động trên chúng mà không cần một thay đổi duy nhất! (Tất nhiên có nhiều cách khác để thực hiện tác dụng tương tự, nhưng đây là MỘT cách để thực hiện)

+0

cảm ơn câu trả lời chi tiết. Các lớp như thay thế cho các cá thể chú thích dẫn xuất phản chiếu thực sự là một trường hợp hợp lệ. Chú thích khá tiện dụng như dữ liệu cấu hình và cho các trường hợp động, khởi tạo chúng là cần thiết. –

+0

"// bỏ qua ngoại lệ để thực hiện gỡ lỗi một chút khó khăn hơn" +1 – Izmaki

-1

Không có trường hợp người dùng hợp lệ cho trình biên dịch đó chỉ làm cho nó trở nên lộn xộn khi cấm và những người viết trình biên dịch có thể cần cơ sở trong một dịp rất hiếm. Nếu bạn cần phân loại chú thích, hãy xem bài viết này để xem cách thực hiện: Why is not possible to extend annotations in Java?

Inagine một linh hồn nghèo đến sau khi bạn duy trì và gỡ lỗi mã đó hoặc một mã khác cần viết công cụ mã hóa và giả định rằng loại chú thích thẳng hoặc người khác chỉ sử dụng chú thích như vậy, thậm chí không mơ ước những gì có thể xảy ra và phải làm gì với nó. Khi phát hiện ra rằng hack và tìm cách để loại bỏ nó anh ta sẽ chết vì thoát vị - hoặc tương đương bệnh :-) Chú thích được cho là báo cáo thuần túy khai báo, chỉ được giải thích bởi một công cụ codegen chạy riêng biệt với mã chú thích và coi đó là dữ liệu.

Hãy nhìn mới về mã và cố gắng thành thật nói một Rason hợp lý cho một cái gì đó như là những gì:

public Class<? extends Annotation> annotationType() { 
    return Id.class; 
} 

và đó là stil một điều nhỏ so với mọi người có thể đặt trong mã.

Chú thích không phải là nơi để thực hành hack - đó là những gì trình biên dịch đang cố truyền đạt. Bạn có biết chính xác thời điểm và cách mã trong "triển khai" chú thích có thể chạy không? Bao gồm CTOR? Cái gì có sẵn và cái gì không ở điểm đó? Điều gì là an toàn để gọi? Trình biên dịch không - nó sẽ có phân tích tĩnh khá nặng cho một trình biên dịch để kiểm tra sự an toàn thực tế của việc hack đó. Vì vậy, thay vào đó nó chỉ đưa ra một cảnh báo để khi một cái gì đó đi sai người ta không thể đổ lỗi cho biên dịch, VM và mọi thứ khác.

+0

Tôi không quan tâm đến việc phân loại chú thích và trường hợp sử dụng của tôi không liên quan đến trình biên dịch. Và tôi không xem xét trường hợp sử dụng của tôi "hack". –

+0

@ZZX, ví dụ của bạn thiếu javadoc, hy vọng giữ câu trả lời cho _why? _. –

+0

Phản ứng quá mức của bạn thực sự không được bảo đảm. Đây là một trường hợp sử dụng rất hợp lệ để thực hiện chú thích: Bạn muốn chạy các bộ xử lý chú thích cung cấp siêu dữ liệu bằng cách sử dụng một api không đáng kể, như javax.lang.model, mà sẽ cố gắng tải các giá trị lớp. Tốt hơn để viết một bộ xử lý chú thích quét các chú thích và tạo ra một trình xây dựng chú thích mà thực sự có thể hoạt động trên các giá trị lớp. (javax.lang.model buộc bạn phải nắm bắt một ngoại lệ thời gian chạy không khai báo và trích xuất loại lớp từ một loại gương; trình tạo được tạo ra sẽ xử lý sự lộn xộn xấu xí đó trong các trình xử lý chú thích khác). – Ajax

4

JAXBIntroductions là một ví dụ hay: nó cho phép định cấu hình chú thích JAXB bằng tệp XML. Hai trường hợp sử dụng chính cần lưu ý: cấu hình các lớp bạn không có quyền truy cập nguồn hoặc các cấu hình khác nhau cho một lớp.

Nói chung, tôi nghĩ rằng các chú thích instantiating tự động chuyển chúng vào khung công tác thường là trường hợp sử dụng tốt. Tuy nhiên, nếu bạn là nhà thiết kế của khuôn khổ này, tôi chắc chắn sẽ suy nghĩ hai lần mặc dù.

0

Tôi sử dụng nó khi tạo chú thích và muốn sử dụng tùy chọn, bằng cách cung cấp mặc định.

Để sử dụng ví dụ của bạn; Khi tôi xử lý/introspect đậu sau, tôi muốn sử dụng một mặc định cho BeanB.

@Id 
class BeanA {} 

// No annotation 
class BeanB {} 

Triển khai mặc định;

private static final Id DEFAULT_ID = new Id() { 

    @Override 
    public IDType value() { 
     return IDType.LOCAL; 
    } 

    @Override 
    public Class<? extends Annotation> annotationType() { 
     return Id.class; 
    } 
}; 

Đang xử lý;

Id beanId = (bean.getClass().isAnnotationPresent(Id.class)) 
    ? bean.getClass().getAnnotation(Id.class) 
    : DEFAULT_ID; 
Các vấn đề liên quan