2009-11-18 44 views
11

Có cách nào để yêu cầu rằng một lớp có một hàm tạo mặc định (không có tham số), sang một bên từ việc sử dụng kiểm tra phản chiếu như sau không? (sau đây sẽ làm việc, nhưng nó hacky và phản ánh chậm)Yêu cầu một hàm tạo mặc định trong java?

boolean valid = false; 
for(Constructor<?> c : TParse.class.getConstructors()) 
{ 
    if(c.getParameterTypes().length == 0) { 
     valid = true; 
     break; 
    } 
} 
if(!valid) 
    throw new MissingDefaultConstructorException(...); 
+0

Thậm chí tồi tệ hơn là "hacky" và chậm là nó sẽ không gây ra một lỗi biên dịch! – tster

+0

Đáng buồn thay trong Java không có cách nào để phát hành một lỗi trình biên dịch cho các trường hợp như vậy khi bạn đang tải các lớp học nằm ngoài tầm kiểm soát của bạn. – Alfonso

Trả lời

21

Bạn có thể xây dựng một bộ xử lý Chú cho điều đó. Bộ xử lý chú thích là các plugin trình biên dịch có thể chạy lúc biên dịch. Lỗi của chúng xuất hiện dưới dạng lỗi trình biên dịch và thậm chí có thể tạm dừng bản dựng.

Dưới đây là một số mẫu mã (Tôi không chạy nó mặc dù):

@SupportedAnnotationTypes("*") // needed to run on all classes being compiled 
@SupportedSourceVersion(SourceVersion.RELEASE_6) 
public class DefaultConstructor extends AbstractProcessor { 

    @Override 
    public boolean process(Set<? extends TypeElement> annotations, 
      RoundEnvironment roundEnv) { 

     for (TypeElement type : ElementFilter.typesIn(roundEnv.getRootElements())) { 
      if (requiresDefaultConstructor(type)) 
       checkForDefaultConstructor(type); 
     } 
     return false; 
    } 

    private void checkForDefaultConstructor(TypeElement type) { 
     for (ExecutableElement cons : 
      ElementFilter.constructorsIn(type.getEnclosedElements())) { 
      if (cons.getParameters().isEmpty()) 
       return; 
     } 

     // Couldn't find any default constructor here 
     processingEnv.getMessager().printMessage(
       Diagnostic.Kind.ERROR, "type is missing a default constructor", 
       type); 
    } 

    private boolean requiresDefaultConstructor(TypeElement type) { 
     // sample: require any JPA Entity to have a default constructor 
     return type.getAnnotation(Entity.class)) != null 
       || type.getQualifiedName().toString().contains("POJO"); 
    } 

} 

Bộ xử lý chú thích trở nên dễ dàng hơn nếu bạn giới thiệu một chú thích (ví dụ RequiresDefaultAnnotation).

Tuyên bố yêu cầu của việc có một vòng loại mặc định

:: Tôi cũng giả định rằng OP yêu cầu cho một cơ chế có thể ngăn chặn các lỗi ngẫu nhiên cho các nhà phát triển, đặc biệt là được viết bởi một người khác. ::

Phải có một cơ chế để khai báo lớp nào yêu cầu một bộ xử lý mặc định. Hy vọng rằng, bạn đã có một tiêu chuẩn cho điều đó, cho dù đó là một mẫu trong tên, mẫu trong vòng loại, một chú thích có thể, và/hoặc một kiểu cơ sở. Trong mẫu tôi đã cung cấp ở trên, bạn có thể chỉ định tiêu chí trong phương thức requiresDefaultConstructor(). Dưới đây là ví dụ về cách có thể thực hiện:

  1. Dựa trên mẫu tên. TypeElement cung cấp quyền truy cập vào tên gói và tên đầy đủ.

    return type.getQualifiedName().toString().contains("POJO"); 
    
  2. Dựa trên chú thích có trong khai báo kiểu. Ví dụ: tất cả các lớp Java Bean Entity phải có các hàm tạo không mặc định

    return type.getAnnotation(Entity.class) != null; 
    
  3. Dựa trên lớp trừu tượng hoặc giao diện.

    TypeElement basetype = processingEnv.getElements().getTypeElement("com.notnoop.mybase"); 
    return processingEnv.getTypes().isSubtype(type.asType(), basetype.asType()); 
    
  4. [Đề xuất cách tiếp cận]: Nếu bạn đang sử dụng giao diện basetype, tôi khuyên bạn nên pha trộn các cách tiếp cận chú thích với giao diện kiểu cơ sở. Bạn có thể khai báo chú thích, ví dụ: MyPlain, cùng với chú giải meta: @Inherited. Sau đó, bạn có thể chú thích loại cơ sở với chú thích đó, sau đó tất cả các lớp con sẽ kế thừa chú thích. Sau đó, phương pháp của bạn sẽ chỉ được

    return type.getAnnotation(MyPlain.class) != null; 
    

    Điều này là tốt bởi vì đó là một chút cấu hình hơn, nếu mô hình thực sự là dựa trên loại hệ thống cấp bậc, và bạn sở hữu lớp gốc.

Như đã đề cập trước đó, chỉ vì nó được gọi là "xử lý chú thích", điều đó có nghĩa là bạn phải sử dụng chú thích! Cách tiếp cận nào trong danh sách bạn muốn theo dõi phụ thuộc vào ngữ cảnh của bạn. Về cơ bản, điểm là bất kỳ logic nào bạn muốn cấu hình trong các công cụ thực thi triển khai của mình, logic đó sẽ đi vào requiresDefaultConstructor.

Lớp học bộ vi xử lý sẽ chạy trên

xử lý Chú gọi trên bất kỳ lớp học nhất định phụ thuộc vào SupportedAnnotationTypes. Nếu chú thích meta SupportedAnnotationTypes chỉ định chú thích cụ thể, thì bộ xử lý sẽ chỉ chạy trên các lớp chứa chú thích đó.

Nếu SupportedAnnotationTypes"*" thì trình xử lý sẽ được gọi trên tất cả các lớp, được chú thích hay không! Kiểm tra các [Javadoc] (http://java.sun.com/javase/6/docs/api/javax/annotation/processing/Processor.html#getSupportedAnnotationTypes()), trong đó nêu:

Cuối cùng, "*" tự đại diện cho bộ của tất cả các loại chú thích, bao gồm tập rỗng Lưu ý rằng một bộ xử lý không nên tuyên bố "*" trừ khi nó được. thực sự xử lý tất cả các file; tuyên bố chú thích không cần thiết có thể gây ra một sự suy giảm hiệu suất trong một số môi trường

Xin lưu ý cách.được trả lại để đảm bảo rằng bộ xử lý không yêu cầu tất cả chú thích.

+0

Có, ngoại trừ việc đó sẽ yêu cầu chú thích được đặt trên lớp (hoặc hàm tạo). Và làm thế nào bạn sẽ thực thi điều đó? :-) – ChssPly76

+0

Chú thích hoạt động khi bạn cung cấp một lớp cơ sở hoặc giao diện phải được sử dụng. Đây là một giải pháp khá thanh lịch cho vấn đề mà người ta không thể thực thi một nhà xây dựng trong một giao diện. Ngoài ra, giải pháp của bạn sử dụng try/catch là cách tao nhã nhất mà tôi biết để xử lý việc này. Mặc dù điều này chỉ hoạt động trong thời gian chạy. – Alfonso

+0

@ ChssPly76, gần như chính xác. Anh ta có thể tạo ra bất kỳ tiêu chuẩn nào mà anh ta muốn tìm hiểu xem một lớp cần phải có một hàm tạo mặc định hay không. – notnoop

6

số Vui lòng cung trên có thể được viết lại dễ dàng hơn như:

try { 
    MyClass.newInstance(); 
} catch (InstantiationException E) { 
    // no constructor 
} catch (IllegalAccessException E) { 
    // constructor exists but is not accessible 
? 
+0

Ý tưởng hay, không nghĩ về điều đó. Nếu tôi quyết định thực thi giá trị thực sự của nó, tôi có lẽ sẽ làm theo cách đó. Nó không thực sự cần thiết để thực thi nó cho mục đích của tôi mặc dù, tôi chỉ tò mò. – jdc0589

+0

Tôi thực sự thi hành nó cho một số trình cắm thêm của bên thứ ba mà chúng tôi sử dụng. Trên (trong số các kiểm tra khác) chạy như một phần của quá trình xác minh trong kịch bản triển khai. Tôi rất quan tâm để xem liệu giải pháp của notnoop có bật ra hay không, mặc dù (không có phụ thuộc chú thích) - nó chắc chắn sẽ làm cho mọi thứ trở nên dễ dàng hơn. – ChssPly76

+0

ngoại trừ cách tiếp cận này thực sự chạy hàm tạo nếu nó hiện diện. Một tác dụng phụ bạn có thể không muốn –

1

Bạn có thể sử dụng PMD và Macker để đảm bảo nguyên tắc kiến ​​trúc. Trong phần mở đầu, Macker kích hoạt các lỗi biên dịch, phá vỡ quy trình xây dựng của bạn khi xác nhận không thành công.

Cá cược mở rộng một số khái niệm được PMD phổ biến về việc xác thực mã nguồn. Một ví dụ tốt là khi bạn muốn đảm bảo rằng tất cả các lớp từ một gói thực hiện một giao diện nhất định.

Vì vậy, nếu bạn rất hoang tưởng (như tôi!) Về việc xác minh tất cả các quy tắc kiến ​​trúc có thể có, Macker thực sự hữu ích.

http://innig.net/macker/

Lưu ý: Trang web không tốt. Màu sắc sẽ làm tổn thương đôi mắt của bạn ... nhưng những công cụ này rất hữu ích.

Richard Gomes http://www.jquantlib.org/

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