2012-01-13 37 views
7

Vấn đề: Tôi đã là một giao diện chức năng Object định nghĩa trong một lớp:Generics: làm thế nào để thực thi hạn chế giữa các phím và các giá trị trong một bản đồ

public static interface FunctionObject<T> { 
     void process(T object); 
    } 

tôi cần nó chung chung bởi vì tôi muốn sử dụng T phương pháp trong quá trình triển khai.
Sau đó, trong lớp generic khác, tôi đã một bản đồ nơi tôi có các lớp học như chìa khóa và đối tượng chức năng như các giá trị:

Map<Class<T>, FunctionObject<T>> map; 

Nhưng tôi cũng muốn bản đồ để chấp nhận các lớp học kiểu phụ và các đối tượng chức năng của siêu kiểu CỦA KEY TYPE, vì vậy tôi đã làm điều này:

Map<Class<? extends T>, FunctionObject<? super T>> map; //not what I need 

ý tưởng cơ bản là để có thể sử dụng bản đồ như sau:

//if T were Number, this should be legal 
    map.put(Class<Integer>, new FunctionObject<Integer>(){...}); 
    map.put(Class<Float>, new FunctionObject<Number>(){...}); 
    map.put(Class<Double>, new FunctionObject<Object>(){...}); 

như tôi muốn thực thi FunctionObject có loại khóa lớp hoặc siêu kiểu, những gì tôi thực sự muốn xác định là:

Map<Class<E extends T>, FunctionObject<? super E>>> map; 

Làm cách nào để đạt được hiệu quả mong muốn? Là một container không đồng nhất typesafe là lựa chọn duy nhất? Các loại bản đồ chung chung sẽ trông như thế nào để cho phép nhập nó từ một tham chiếu?

Trả lời

4

parametrized container, dường như chỉ làm việc tốt:

public class MyMap<T> 
{ 
    interface FunctionObject<X> {} 

    private Map<Class<? extends T>, FunctionObject<Object>> map = new HashMap<>(); 

    @SuppressWarnings("unchecked") 
    public <E extends T> void put(Class<E> c, FunctionObject<? super E> f) 
    { 
     map.put(c, (FunctionObject<Object>) f); 
    } 

    public <E extends T> FunctionObject<Object> get(Class<E> c) 
    { 
     return map.get(c); 
    } 

    public static void Main(String[] args) 
    { 
     MyMap<Number> map = new MyMap<>(); 

     map.put(Integer.class, new FunctionObject<Integer>() {}); 
     map.put(Float.class, new FunctionObject<Number>() {}); 
     map.put(Double.class, new FunctionObject<Object>() {}); 
    } 
} 

Sửa tuân thủ cho câu hỏi. Đáng buồn là không có cách nào để tránh downcasting đối tượng.

Chỉnh sửa thêm get().

+0

Có thể hoạt động tùy thuộc vào trường hợp sử dụng. –

+0

Có, nhưng thực hiện nhận được là khi niềm vui bắt đầu XD. –

+0

'get()' chỉ có thể trả về 'FunctionObject ()': vì tham số là một * superclass * của T và khóa, không có cách nào để thiết lập một ràng buộc trên. Tôi đã thêm nó vào câu trả lời. – Viruzzo

4

Bạn có thể thực hiện việc này bằng cách đóng gói, giả sử bạn chỉ sử dụng bản đồ thông qua phương pháp kiểm tra điều này trên cơ sở mỗi lần nhập cảnh.

Phương pháp thêm sau đây cũng không cần phải tăng gấp đôi trên loại.

public class Main { 
interface FunctionObject<T> { } 

private final Map<Class, FunctionObject> map = new LinkedHashMap<Class, FunctionObject>(); 

public <T> void add(FunctionObject<T> functionObject) { 
    Class<T> tClass = null; 
    for (Type iType : functionObject.getClass().getGenericInterfaces()) { 
     ParameterizedType pt = (ParameterizedType) iType; 
     if (!pt.getRawType().equals(FunctionObject.class)) continue; 
     Type t = pt.getActualTypeArguments()[0]; 
     tClass = (Class<T>) t; 
     break; 
    } 
    map.put(tClass, functionObject); 
} 

public <T> void put(Class<T> tClass, FunctionObject<T> functionObject) { 
    map.put(tClass, functionObject); 
} 

public <T> FunctionObject<T> get(Class<T> tClass) { 
    return map.get(tClass); 
} 

public static void main(String... args) throws IOException { 
    Main m = new Main(); 
    m.add(new FunctionObject<Integer>() { 
    }); 
    FunctionObject<Integer> foi = m.get(Integer.class); 
    System.out.println(foi.getClass().getGenericInterfaces()[0]); 
} 
} 

in

Main.Main$FunctionObject<java.lang.Integer> 

Bạn có thể sử dụng @SuppressWarnings("unchecked") nếu bạn muốn vô hiệu hóa các cảnh báo.

Vấn đề là; không có cách nào để mô tả ràng buộc bạn có trong khai báo trường, bạn có thể đạt được kết quả tương tự nếu bạn sử dụng các phương thức accessor thực hiện kiểm tra trên cơ sở mỗi mục nhập. Bạn cũng có thể thêm kiểm tra thời gian chạy nếu bạn cần đảm bảo các loại thô là chính xác.

+0

Nhưng điều này chỉ cho phép loại T và không có gì khác, phải không? Và nhân tiện, việc kê khai Bản đồ đó đưa ra cảnh báo về việc sử dụng các loại thô. –

+5

Vâng, đó là điểm của câu trả lời của Peter: nó không phải là bản đồ đảm bảo sự an toàn kiểu. Thực tế là cách duy nhất để đặt/lấy các giá trị trong/từ bản đồ là sử dụng các phương thức này, luôn luôn kết hợp một hàm Funtion với một lớp . Tất nhiên, nếu bạn sử dụng các kiểu thô khi gọi các phương thức này, bạn không có bất kỳ sự bảo đảm nào. –

+0

Ok, do đó, không có lựa chọn nào khác ngoài việc mã hóa một thùng chứa an toàn. Tôi tự hỏi, nếu điều này là như nhau trong C#. –

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