2017-06-07 14 views
7

Tôi muốn tạo một IdentityHashMap<Class<T>, Consumer<T>>. Về cơ bản, tôi muốn ánh xạ một kiểu với một phương thức nói phải làm gì với kiểu này.Người tiêu dùng <T> ánh xạ Lớp <T> trong HashMap

tôi muốn tự động có thể nói với các đối tượng X, thực hiện Y. Tôi có thể làm

private IdentityHashMap<Class<?>, Consumer<?>> interceptor = new IdentityHashMap<>(); 

nhưng nó sucks vì sau đó tôi phải cast đối tượng trong lamba khi sử dụng nó.

Ví dụ:

interceptor.put(Train.class, train -> { 
    System.out.println(((Train)train).getSpeed()); 
}); 

Những gì tôi muốn làm là

private <T> IdentityHashMap<Class<T>, Consumer<T>> interceptor = new IdentityHashMap<>(); 

Nhưng nó dường như không được cho phép. Có cách nào để làm việc này không ? Cách giải quyết tốt nhất cho các loại bản đồ với phương thức cho loại này là gì?

+3

Đây là một đèn ở bên hông , nhưng lưu ý rằng 'java.lang.Class' định nghĩa bình đẳng là nhận dạng, vì vậy không cần sử dụng' IdentityHashMap' tại đây. – ruakh

+0

@ruakh Không cần sử dụng 'HashMap'. Điều gì sẽ mang lại để có thêm 2 cuộc gọi 'equals' khi so sánh? – Winter

+1

Vui lòng xem http://docs.oracle.com/javase/8/docs/api/java/util/IdentityHashMap.html. Bạn chỉ nên sử dụng 'IdentityHashMap' khi bạn cần nó. – ruakh

Trả lời

6

Đây thực chất là giống như kiểu an chứa không đồng nhất, có lẽ ban đầu được mô tả bởi Joshua Bloch, ngoại trừ bạn không thể sử dụng Class bỏ kết quả.

thật là thú vị, tôi không thể tìm thấy một ví dụ tuyệt vời hiện có trên SO, vì vậy đây là một trong:

package mcve; 
import java.util.*; 
import java.util.function.*; 

class ClassToConsumerMap { 
    private final Map<Class<?>, Consumer<?>> map = 
     new IdentityHashMap<>(); 

    public <T> Consumer<? super T> put(Class<T> key, Consumer<? super T> c) { 
     @SuppressWarnings("unchecked") 
     final Consumer<? super T> old = 
      (Consumer<? super T>) map.put(key, c); 
     return old; 
    } 

    public <T> Consumer<? super T> get(Class<T> key) { 
     @SuppressWarnings("unchecked") 
     final Consumer<? super T> c = 
      (Consumer<? super T>) map.get(key); 
     return c; 
    } 
} 

Nếu đó là cần thiết để sử dụng các loại chung chung, như Consumer<List<String>>, sau đó bạn cần phải sử dụng một cái gì đó giống như ổi TypeToken bởi vì Class chỉ có thể đại diện cho việc xóa một loại.

Một điều khó chịu về những hạn chế của Generics Java là một trong những không thể được viết tổng quát, bởi vì không có cách nào để làm ví dụ:

class ClassToGenericValueMap<V> { 
    ... 
    public <T> V<T> put(Class<T> key, V<T> val) {...} 
    public <T> V<T> get(Class<T> key) {...} 
} 

Nhưng dù sao, đó là làm thế nào để làm điều này loại .

Lưu ý phụ, Ổi có số ClassToInstanceMap khi bạn cần Map<Class<T>, T>.

1

Tôi chỉ có thể để cho các IdentityHashMap với bình thường Class<?>Consumer<?>

private IdentityHashMap<Class<?>, Consumer<?>> interceptor = new IdentityHashMap<>(); 

Và rồi tôi quấn các hoạt động đặt trong một phương pháp. Phương pháp này chấp nhận một loại và một người tiêu dùng của cùng một chung chung.

public <T> void intercept(Class<T> type, Consumer<T> consumer) 
{ 
    interceptor.put(type, consumer); 
} 

này cho phép tôi viết

intercept(Train.class, train -> { 
    System.out.println(train.getSpeed()); 
}); 
5

có thể thực hiện điều này theo cách an toàn loại mà không có bất kỳ ô được bỏ chọn. Các giải pháp nằm trong gói các Consumer<T> thành một tổng quát hơn Consumer<Object> rằng phôi và sau đó các đại biểu cho người tiêu dùng ban đầu:

public class ClassToConsumerMap { 
    private final Map<Class<?>, Consumer<Object>> map = new IdentityHashMap<>(); 

    public <T> Consumer<? super T> put(Class<T> key, Consumer<? super T> c) { 
     return map.put(key, o -> c.accept(key.cast(o))); 
    } 

    public <T> Consumer<? super T> get(Class<T> key) { 
     return map.get(key); 
    } 
} 

Tùy thuộc vào nhu cầu của bạn, get() cũng có thể chỉ đơn giản là trả lại một Consumer<Object>. Điều này sẽ là cần thiết nếu bạn chỉ biết loại thời gian chạy, ví dụ:

classToConsumerMap.get(someObject.getClass()).accept(someObject); 

Tôi khá chắc chắn tôi thấy giải pháp này (hoặc một cái gì đó tương tự) trong một cuộc nói chuyện @ Devoxx Bỉ năm 2016, có thể từ Venkat Subramaniam, nhưng tôi dứt khoát không thể tìm thấy nó trở lại ...

+0

Điều này thực sự tốt, nhưng nó không phải luôn luôn là một giải pháp chung. Ví dụ, sẽ khó sử dụng mẫu này hơn nếu chúng ta có 'Bản đồ , Danh sách >' hoặc một số giá trị khác mà chúng ta không thể dễ dàng tạo bộ điều hợp. – Radiodef

+1

@radiodef thực sự, đó sẽ luôn luôn là một trường hợp theo từng trường hợp. Tôi muốn có một cách sạch hơn để thực hiện một số kiểu chung chung mà không cần cảnh báo. –

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