Java Generics Wildcarding với nhiều lớp


385

Tôi muốn có một đối tượng Class, nhưng tôi muốn buộc bất kỳ lớp nào nó đại diện để mở rộng lớp A và thực hiện giao diện B.

Tôi có thể làm:

Class<? extends ClassA>

Hoặc là:

Class<? extends InterfaceB>

nhưng tôi không thể làm cả hai. Có cách nào để làm việc này không?

Câu trả lời:


613

Trên thực tế, bạn có thể làm những gì bạn muốn. Nếu bạn muốn cung cấp nhiều giao diện hoặc một lớp cộng với giao diện, bạn phải có ký tự đại diện của mình trông giống như thế này:

<T extends ClassA & InterfaceB>

Xem Hướng dẫn Generics tại sun.com, cụ thể là phần Tham số loại giới hạn , ở cuối trang. Bạn thực sự có thể liệt kê nhiều giao diện nếu bạn muốn, sử dụng & InterfaceNamecho từng giao diện mà bạn cần.

Điều này có thể nhận được phức tạp tùy ý. Để chứng minh, hãy xem khai báo JavaDoc của Collections#max(được gói trên hai dòng) là:

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

sao phức tạp vậy Như đã nói trong Câu hỏi thường gặp về Java Generics: Để duy trì khả năng tương thích nhị phân .

Có vẻ như điều này không hoạt động để khai báo biến, nhưng nó hoạt động khi đặt một ranh giới chung trên một lớp. Vì vậy, để làm những gì bạn muốn, bạn có thể phải nhảy qua một vài vòng. Nhưng bạn có thể làm điều đó. Bạn có thể làm một cái gì đó như thế này, đặt một ranh giới chung cho lớp của bạn và sau đó:

class classB { }
interface interfaceC { }

public class MyClass<T extends classB & interfaceC> {
    Class<T> variable;
}

để có được variableđiều đó có những hạn chế mà bạn muốn. Để biết thêm thông tin và ví dụ, hãy xem trang 3 của Generics trong Java 5.0 . Lưu ý, trong <T extends B & C>, tên lớp phải đến trước và giao diện theo sau. Và tất nhiên bạn chỉ có thể liệt kê một lớp duy nhất.


94
Điều này rất hữu ích. Điều đáng nói là lớp phải đến trước, bạn không thể nói '<T mở rộng InterfaceB & ClassA>'.
EricS

5
Làm thế nào để bạn làm tương tự cho T nên mở rộng một lớp HOẶC thực hiện một giao diện?
Ragunath Jawahar

1
@RagunathJawahar: Bạn không thể quá cởi mở về điều này. Bạn phải có một số ranh giới và một trong số đó là biết trước nơi bạn sẽ có giao diện và nơi bạn sẽ có các lớp và cách bạn sẽ sử dụng tính kế thừa. Bạn khá nhiều phải biết cho một tham số loại nếu nó sẽ là một lớp hoặc một giao diện.
Eddie

4
Dòng đầu tiên của câu trả lời của bạn nói rằng "hãy để ký tự đại diện của bạn trông giống như thế này." Không có ?biểu hiện đó, vậy nó có thực sự là "ký tự đại diện" không? Tôi yêu cầu bởi vì tôi không thể làm cho toàn bộ khái niệm "mở rộng hai thứ cùng một lúc" hoạt động khi tham số loại thực sự là ký tự đại diện.
The11

1
Vâng, nó không thực sự là một ký tự đại diện và bạn không thể làm những gì OP yêu cầu với một ký tự đại diện. Bạn có thể làm những gì OP muốn, một cách hiệu quả, không chỉ với một ký tự đại diện.
Eddie

17

Bạn không thể làm điều đó với các tham số loại "ẩn danh" (nghĩa là các ký tự đại diện sử dụng ?), nhưng bạn có thể làm điều đó với các tham số loại "được đặt tên". Đơn giản chỉ cần khai báo tham số loại ở mức phương thức hoặc lớp.

import java.util.List;
interface A{}
interface B{}
public class Test<E extends B & A, T extends List<E>> {
    T t;
}

2
Tại sao không cho phép ký tự đại diện? tức là tôi không thấy lý do tại sao điều này không hợp lệ: ArrayList <? mở rộng danh sách ClassA & InterfaceB>; Và sau đó nếu bạn rút một phần tử ra khỏi danh sách, bạn có thể gán nó cho một biến loại ClassA hoặc loại InterfaceB
Đánh dấu

Thay thế ký tự đại diện bằng một biến là một kỹ thuật tuyệt vời! Nhưng nó không luôn luôn hoạt động. Ví dụ, Java không cho phép các biến loại tên trong các loại giá trị chú thích, trong khi nó cho phép các ký tự đại diện.
sigpwned
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.