泛型实践:如何实现类型安全的HashMap<Class<?>, CachedObject<?>>
解决泛型缓存的类型安全问题
你的核心问题是在缓存泛型类型的CachedObject时,无法避免编译器的不安全转换警告——虽然实际转换是安全的,但想要更优雅的类型安全实现。下面提供几个可行的方案:
方案一:利用Class.cast()消除警告
直接用传入的Class<T>对象的cast方法做类型转换,编译器会认可这种转换的安全性,无需强制类型转换语法,也能消除警告:
private final HashMap<Class<?>, CachedObject<?>> cachedAttributes = new HashMap<>(); public <T extends PrintServiceAttribute> T getAttribute(Class<T> category) { if (!cachedAttributes.containsKey(category)) { Supplier<T> supplier = () -> innerPrintService.getAttribute(category); CachedObject<T> cachedObject = new CachedObject<>(supplier, lifespan); cachedAttributes.put(category, cachedObject); } return category.cast(cachedAttributes.get(category).get()); }
方案二:封装类型安全的缓存容器
把缓存逻辑封装到内部类中,在容器内部处理类型转换,对外暴露类型安全的API,业务代码里就不会出现警告:
private static class TypeSafeAttributeCache { private final HashMap<Class<?>, CachedObject<?>> cache = new HashMap<>(); public <T extends PrintServiceAttribute> void put(Class<T> key, CachedObject<T> value) { cache.put(key, value); } @SuppressWarnings("unchecked") public <T extends PrintServiceAttribute> CachedObject<T> get(Class<T> key) { // 转换安全:仅允许存入key与value类型匹配的项 return (CachedObject<T>) cache.get(key); } } private final TypeSafeAttributeCache cachedAttributes = new TypeSafeAttributeCache(); public <T extends PrintServiceAttribute> T getAttribute(Class<T> category) { if (cachedAttributes.get(category) == null) { Supplier<T> supplier = () -> innerPrintService.getAttribute(category); CachedObject<T> cachedObject = new CachedObject<>(supplier, lifespan); cachedAttributes.put(category, cachedObject); } return cachedAttributes.get(category).get(); }
方案三:用computeIfAbsent简化代码
结合Java 8的Map.computeIfAbsent方法简化缓存初始化逻辑,同时配合Class.cast保证类型安全:
private final HashMap<Class<?>, CachedObject<?>> cachedAttributes = new HashMap<>(); public <T extends PrintServiceAttribute> T getAttribute(Class<T> category) { CachedObject<?> cached = cachedAttributes.computeIfAbsent(category, k -> { Supplier<T> supplier = () -> innerPrintService.getAttribute(category); return new CachedObject<>(supplier, lifespan); }); return category.cast(cached.get()); }
原代码转换安全的原因
存入缓存时,Class<T>和对应的CachedObject<T>是严格一一对应的:用Class<T>作为key存入时,value必然是持有T类型的CachedObject<T>。所以无论用强制转换还是Class.cast,运行时都不会出现类型转换异常,只是编译器无法在编译期验证这一点。
内容的提问来源于stack exchange,提问作者Kyle Berezin
相关产品推荐
相关产品推荐

