2012-04-27 38 views
15

Tôi đang viết một đơn giản HashMap cache dựa trên đó hoạt động như sau:Java - Mở rộng HashMap - Object vs Generics hành vi

  1. Nếu yêu cầu key trong bộ nhớ cache, trở value của nó.
  2. Nếu yêu cầu keykhông phải là ở đó, chạy một phương pháp sản xuất value dựa trên key, lưu trữ cả, trở về value.

Mã:

import java.util.HashMap; 

abstract class Cache<K, V> extends HashMap<K, V> { 
    @Override 
    public V get(Object key) { 
     if (containsKey(key)) { 
      return super.get(key); 
     } else { 
      V val = getData(key); 
      put((K)key, val); // this is the line I'm discussing below 
      return val; 
     } 
    } 

    public abstract V getData(Object key); 
} 

Nó khá đơn giản và hoạt động tốt. Tuy nhiên, tôi ghét quyết định của Sun đối với get() để tham số Object làm đối số của nó chứ không phải K. Tôi đã đọc đủ về nó để biết rằng nó có một số lý do đằng sau nó (mà tôi không đồng ý, nhưng đó là một câu chuyện khác).

Sự cố của tôi nằm trong dòng nhận xét, vì có vẻ như dàn diễn viên không được chọn. Do loại tẩy xoá, không có cách nào tôi có thể kiểm tra xem key có thuộc loại K (cần thiết cho chức năng put() thích hợp) và do đó, phương pháp dễ bị lỗi.

Một giải pháp sẽ là chuyển từ "là" thành "có mối quan hệ" HashMap đẹp hơn và sạch sẽ hơn, nhưng sau đó Cache không thể triển khai Map. Mã này:

import java.util.HashMap; 
import java.util.Map; 

abstract class Cache<K, V> { 
    private final Map<K, V> map = new HashMap<K, V>(); 

    public V get(K key) { 
     if (map.containsKey(key)) { 
      return map.get(key); 
     } else { 
      V val = getData(key); 
      map.put(key, val); 
      return val; 
     } 
    } 

    public abstract V getData(K key); 
} 

bất cứ ai có thể đưa ra bất kỳ khác (thậm chí hackish) giải pháp, vì vậy mà tôi có thể duy trì Cache là một Map và vẫn được an toàn trong các điều khoản của get(Object key)put(K key, V val) loại?

Điều duy nhất tôi có thể nghĩ là tạo một phương pháp khác có tên là getValue(Key k) sẽ ủy quyền cho get(Object key), nhưng sau đó tôi không thể buộc bất kỳ ai sử dụng phương pháp mới thay vì phương pháp thông thường.

+1

Bạn có thể triển khai 'getA' hoặc' myget' Bạn không thể thay đổi hành vi của Bản đồ.get() vì bạn không thể ép buộc mã sử dụng giao diện thay vì lớp của bạn trực tiếp để được biên dịch lại. –

Trả lời

15

Không. Bạn đã tìm thấy giải pháp đúng trong việc chuyển sang mối quan hệ "có một". (Thành thật mà nói, có phương pháp get tính giá trị mới nếu giá trị chưa tồn tại là đáng ngạc nhiên, vi phạm hợp đồng Map và có thể dẫn đến hành vi cực kỳ kỳ lạ đối với một số phương pháp khác. từ MapMaker, cung cấp gần như hành vi chính xác này - bởi vì nó chỉ là như vậy riddled với các vấn đề.)

Điều đó nói rằng, ví dụ Số điện thoại Cache của ổi là hiển thị Map<K, V> asMap()xem, đó là điều bạn có thể làm. Điều đó mang lại cho bạn hầu hết các ưu điểm của Map mà không ảnh hưởng đến an toàn loại.

+0

Có vẻ như 'MapMaker' thực sự đã làm những gì tôi làm :). Tôi biết một phần về hành vi kỳ lạ. Phương thức 'V getData (khóa K)' là 'abstract' và do đó được dự định chỉ được chỉ định (được triển khai) trên instantiation Cache. Tôi thậm chí đã cố gắng làm cho lớp 'cuối cùng trừu tượng' (mặc dù tôi biết 100% rằng nó sẽ không hoạt động) hay chỉ là 'final' (với phương thức overridable bởi lớp bên trong vô danh - không, tất nhiên, làm việc) để đảm bảo không ai sẽ cố gắng phân lớp đoạn mã nguy hiểm này với một số logic nghiệp vụ. Dù sao, cảm ơn rất nhiều, mối quan hệ "có-một" với 'asMap()' cho một _view_ nó là. –

+0

Nhân tiện, tôi không thể tin rằng mình đã bỏ lỡ bộ nhớ Cache của ổi. Tôi sẽ không sử dụng nó ngay bây giờ (khi tôi có thực hiện của riêng tôi) tự hào, nhưng nếu tôi biết rằng ngày hôm qua ... :) –

+5

Bạn ... nên xem xét sử dụng 'Cache' của ổi anyway. Nó được sử dụng trong quá trình sản xuất của Google, vì vậy nó được kiểm tra và tối ưu hóa rất nhiều và nó có [rất nhiều] (http://code.google.com/p/guava-libraries/wiki/CachesExplained) các tính năng tiện dụng. –

0

chắc chắn mối quan hệ có một là triển khai chính xác. logic nghiệp vụ về cách giá trị được tạo ra phải được loại bỏ khỏi lớp bộ nhớ cache.

+0

Đó là phần thú vị về nó - không có logic! Phương thức này là 'abstract' và do đó chỉ nên được chỉ định (được triển khai, overriden - tùy theo bạn thích nhất) trên instantiation Cache. Tôi thậm chí đã cố gắng làm cho lớp 'cuối cùng trừu tượng' (mặc dù tôi biết 100% rằng nó sẽ không hoạt động) hay chỉ là 'final' (với phương thức overridable bởi lớp bên trong vô danh - không, tất nhiên, làm việc) để đảm bảo không ai sẽ cố gắng phân lớp đoạn mã nguy hiểm này với một số logic nghiệp vụ. –

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