2008-10-13 32 views
43

Vì vậy, tôi hiểu rằng những điều sau đây không hiệu quả, nhưng tại sao không hoạt động?Tại sao tôi không thể sử dụng đối số loại trong thông số loại có nhiều giới hạn?

interface Adapter<E> {} 

class Adaptulator<I> { 
    <E, A extends I & Adapter<E>> void add(Class<E> extl, Class<A> intl) { 
     addAdapterFactory(new AdapterFactory<E, A>(extl, intl)); 
    } 
} 

Phương pháp add() mang lại cho tôi một lỗi biên dịch, "Không thể chỉ định bất kỳ Adaptor ràng buộc thêm <E> khi lần đầu tiên ràng buộc là một tham số kiểu" (trong Eclipse), hoặc "tham số Type có thể không được theo sau bởi giới hạn khác" (trong IDEA), hãy chọn.

Rõ ràng bạn không được phép sử dụng thông số loại I ở đó, trước & và đó là điều đó. (Và trước khi bạn yêu cầu, nó không hoạt động nếu bạn chuyển đổi 'em, bởi vì không có đảm bảo rằng I không phải là một lớp cụ thể.) Nhưng tại sao không? Tôi đã xem qua câu hỏi thường gặp của Angelika Langer và không thể tìm thấy câu trả lời.

Nói chung khi một số giới hạn generics có vẻ tùy ý, đó là vì bạn đã tạo ra một tình huống mà hệ thống kiểu không thể thực thi chính xác. Nhưng tôi không hiểu trường hợp nào sẽ phá vỡ những gì tôi đang cố gắng làm ở đây. Tôi muốn nói có thể nó có liên quan đến phương pháp sau khi xóa, nhưng chỉ có một phương pháp add(), vì vậy nó không giống như bất kỳ sự mơ hồ nào ...

Ai đó có thể giải quyết vấn đề cho tôi?

Trả lời

23

Tôi cũng không chắc chắn lý do tại sao có hạn chế. Bạn có thể thử gửi một e-mail thân thiện tới các nhà thiết kế của Java 5 Generics (chủ yếu là Gilad Bracha và Neal Gafter).

Tôi đoán là họ chỉ muốn hỗ trợ một mức tối thiểu tuyệt đối là intersection types (nghĩa là nhiều giới hạn), để làm cho ngôn ngữ không phức tạp hơn mức cần thiết. Không thể sử dụng giao lộ làm chú thích loại; một lập trình viên chỉ có thể thể hiện một giao lộ khi nó xuất hiện dưới dạng giới hạn trên của biến kiểu.

Và tại sao trường hợp này lại được hỗ trợ? Câu trả lời là nhiều giới hạn cho phép bạn kiểm soát việc xóa, cho phép duy trì khả năng tương thích nhị phân khi mở rộng các lớp hiện có. Như đã giải thích trong phần 17.4 của book bởi Naftalin và Wadler, một phương pháp max sẽ logic có chữ ký sau đây:

public static <T extends Comparable<? super T>> T max(Collection<? extends T> coll) 

Tuy nhiên, điều này xóa để:

public static Comparable max(Collection coll) 

Mà không khớp với chữ ký lịch sử của max và khiến khách hàng cũ bị hỏng. Với nhiều giới hạn, chỉ có trái hầu hết các ràng buộc được xem xét để xóa bỏ hoàn, vì vậy nếu max được đưa ra chữ ký sau đây:

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) 

Sau đó, xóa các chữ ký của nó trở thành:

public static Object max(Collection coll) 

Đó là bằng chữ ký của max trước Generics. Có vẻ hợp lý khi các nhà thiết kế Java chỉ quan tâm đến trường hợp đơn giản này và hạn chế việc sử dụng các loại giao lộ khác (nâng cao hơn) bởi vì họ không chắc chắn về độ phức tạp mà nó có thể mang lại. Vì vậy, lý do cho quyết định thiết kế này không cần phải là một vấn đề an toàn có thể xảy ra (như câu hỏi gợi ý).

Thảo luận thêm về loại giao lộ và hạn chế của generics trong một upcoming OOPSLA paper.

+0

Trên thực tế, nếu điểm của nhiều giới hạn là kiểm soát việc xóa, điều đó hoàn toàn có ý nghĩa, bởi vì tôi sẽ chỉ xóa đối tượng. –

1

Điều này có thể không trả lời câu hỏi gốc, nhưng chỉ muốn chỉ ra rằng spec rõ ràng cấm nó. tìm kiếm Google cho các thông báo lỗi đưa tôi đến this blog entry, mà hơn nữa điểm để jls 4.4:

Các ràng buộc bao gồm hoặc là một loại biến, hoặc một lớp hoặc giao diện kiểu T có thể tiếp theo thêm loại giao diện I1, ... , Trong.

Vì vậy, nếu bạn sử dụng tham số kiểu bị ràng buộc, bạn không thể sử dụng bất kỳ giới hạn nào khác, giống như thông báo lỗi cho biết.

Tại sao lại hạn chế? Tôi không có ý kiến.

+0

Vì tôi có thể là một lớp học? Chỉ vì A mở rộng tôi không có nghĩa là tôi là một giao diện (ok mà sẽ làm cho A một giao diện), nhưng A có thể dễ dàng là một phân lớp của tôi, bị cấm bởi spec –

+0

vấn đề gì nếu tôi là một lớp học ? Bộ chuyển đổi được biết đến là một giao diện. –

9

Đây là một trích dẫn từ JLS:

Các hình thức của một ràng buộc bị hạn chế (chỉ phần tử đầu tiên có thể là một lớp hoặc kiểu dữ liệu, và chỉ có một loại biến thể xuất hiện trong các ràng buộc) để ngăn cản một số tình huống khó xử nhất định tồn tại.

Chính xác những tình huống khó xử đó là gì, tôi không biết.

+1

Có lẽ họ cũng không biết? – Pacerier

12

Có hai lý do có thể cho cấm này:

  1. phức tạp. Sun bug 4899305 cho thấy rằng một ràng buộc có chứa một tham số kiểu cộng với các kiểu tham số bổ sung sẽ cho phép các loại đệ quy phức tạp hơn cả tồn tại. Tóm lại, Bruno's answer.

  2. Khả năng chỉ định các loại bất hợp pháp. Cụ thể, extending a generic interface twice with different parameters. Tôi không thể đưa ra một ví dụ không giả tạo, nhưng:

    /** Contains a Comparator<String> that also implements the given type T. */ 
    class StringComparatorHolder<T, C extends T & Comparator<String>> { 
      private final C comparator; 
      // ... 
    } 
      
    void foo(StringComparatorHolder<Comparator<Integer>, ?> holder) { ... }

Bây giờ holder.comparator là một Comparator<Integer>Comparator<String>. Nó không rõ ràng với tôi chính xác bao nhiêu rắc rối này sẽ gây ra cho trình biên dịch, nhưng nó rõ ràng là không tốt. Giả sử nói riêng rằng Comparator đã có một phương pháp như thế này:

void sort(List<? extends T> list);

chúng tôi Comparator<Integer>/Comparator<String> hybrid hiện nay có hai phương pháp với cùng tẩy xoá:

void sort(List<? extends Integer> list); 
void sort(List<? extends String> list);

Đó là cho các loại lý do mà bạn không thể chỉ định một loại như vậy trực tiếp:

<T extends Comparator<Integer> & Comparator<String>> void bar() { ... }
java.util.Comparator cannot be inherited with different arguments: 
    <java.lang.Integer> and <java.lang.String>

Kể từ <A extends I & Adapter<E>> cho phép bạn làm điều tương tự một cách gián tiếp, đó là ra, quá.

+2

Thú vị. Phải suy nghĩ về điều đó một chút. Dường như trình biên dịch cũng bắt được nó - tại tuyên bố của foo(), dù sao đi nữa. Nhưng có lẽ có một ví dụ thậm chí còn nhiều hơn nữa sẽ làm cho nó rõ ràng hơn. :) James Iry nói rằng nó hoạt động trong Scala: http://www.chrononaut.org/showyourwork/?p=52#comment-46 –

+0

@Chris, liên kết thứ hai của bạn bị gỡ xuống ...... – Pacerier

+0

@Pacerier , cảm ơn. Thật không may, tôi dường như không thể tìm thấy một bản sao trực tiếp của nó ngay bây giờ: ( –

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