Java 泛型和可序列化
Java generics and Serializable
我们创建了一个抽象 class 用于处理 Redis(set/get 值),如下所示:
public abstract class AbstractCachedSupport<T extends Serializable> {
protected T get(CacheKey key, Supplier<T> supplier) {...}
// ...
}
我不满意的是在扩展这个class:
时我们不能使用像List, Map这样的接口
public class CachedMap extends AbstractCachedSupport<Map<String, Integer>>
因为它们不扩展 Serializable,所以我们必须始终使用具体的 classes:
public class CachedMap extends AbstractCachedSupport<HashMap<String, Integer>>
不用说,当从一个具体 class 迁移到另一个具体时,这有其自身的问题。这也不是我称之为最佳实践的东西,但也许这就是我。
另一种让我们可以灵活地使用接口的替代方法是删除有界类型并在运行时检查 T 是否扩展了 Serializable:
public abstract class AbstractCachedSupport<T> {
public AbstractCachedSupport() {
final Class<T> type = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
if (!Serializable.class.isAssignableFrom(type)) {
throw new RuntimeException("T must extend Serializable");
}
}
protected T get(CacheKey key, Supplier<T> supplier) {...}
// ...
}
这让我们没有编译时检查 T 扩展可序列化,这也不是一件好事。
你知道我们可以优雅地解决这个问题的其他方法吗?您愿意使用第一个(有界类型参数)还是第二个(仅运行时检查)?
折衷方案是首先使用容器 class 来存放 collection:
public class IntegerParamsContainer implements Serializable {
private static final long serialVersionUID = 1L;
private Map<String, Integer> map;
}
public class CachedMap extends AbstractCachedSupport<IntegerParamsContainer>
但这最终与第二个问题相同:没有编译时检查,责任在开发人员的肩上,始终使用实现 Serializable 的 collections。
编辑:
扩展 AbstractCachedSupport
的一些 classes 是 Spring(当前版本 4.1)组件 classes 并且它们不被称为 CachedMap
或类似的东西,而是 CityAutocompleteDataBean
, WrParamsDataBean
等..
如果我们向这些组件 classes 添加泛型,我们最终会得到如下声明:
@Inject
private CityAutocompleteDataBean<ArrayList<String>>;
@Inject
private WrParamsDataBean<HashMap<String, WrData>>;
相对于
@Inject
private CityAutocompleteDataBean;
@Inject
private WrParamsDataBean;
使用 <ArrayList<String>>
和 <HashMap<String, WrData>>
的原因大多数开发人员在看到这样的代码行时都会逃避。我也觉得它很丑,考虑到我们从哪里开始以及我们用它做什么。
尽管如此,这是我要求的,谢谢 Jesper。
您可以使用以下语法进行操作:
public abstract class AbstractCachedSupport<T extends Serializable> {
// ...
}
public class CachedMap<T extends Map<String, Integer> & Serializable>
extends AbstractCachedSupport<T> {
// ...
}
这意味着类型 T
必须实现 Map<String, Integer>
以及 Serializable
.
然后您可以将 CachedMap
与实现 Serializable
:
的特定 Map
实现一起使用
CachedMap<HashMap<String, Integer>> cachedMap = new CachedMap<>();
我们创建了一个抽象 class 用于处理 Redis(set/get 值),如下所示:
public abstract class AbstractCachedSupport<T extends Serializable> {
protected T get(CacheKey key, Supplier<T> supplier) {...}
// ...
}
我不满意的是在扩展这个class:
时我们不能使用像List, Map这样的接口public class CachedMap extends AbstractCachedSupport<Map<String, Integer>>
因为它们不扩展 Serializable,所以我们必须始终使用具体的 classes:
public class CachedMap extends AbstractCachedSupport<HashMap<String, Integer>>
不用说,当从一个具体 class 迁移到另一个具体时,这有其自身的问题。这也不是我称之为最佳实践的东西,但也许这就是我。
另一种让我们可以灵活地使用接口的替代方法是删除有界类型并在运行时检查 T 是否扩展了 Serializable:
public abstract class AbstractCachedSupport<T> {
public AbstractCachedSupport() {
final Class<T> type = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
if (!Serializable.class.isAssignableFrom(type)) {
throw new RuntimeException("T must extend Serializable");
}
}
protected T get(CacheKey key, Supplier<T> supplier) {...}
// ...
}
这让我们没有编译时检查 T 扩展可序列化,这也不是一件好事。
你知道我们可以优雅地解决这个问题的其他方法吗?您愿意使用第一个(有界类型参数)还是第二个(仅运行时检查)?
折衷方案是首先使用容器 class 来存放 collection:
public class IntegerParamsContainer implements Serializable {
private static final long serialVersionUID = 1L;
private Map<String, Integer> map;
}
public class CachedMap extends AbstractCachedSupport<IntegerParamsContainer>
但这最终与第二个问题相同:没有编译时检查,责任在开发人员的肩上,始终使用实现 Serializable 的 collections。
编辑:
扩展 AbstractCachedSupport
的一些 classes 是 Spring(当前版本 4.1)组件 classes 并且它们不被称为 CachedMap
或类似的东西,而是 CityAutocompleteDataBean
, WrParamsDataBean
等..
如果我们向这些组件 classes 添加泛型,我们最终会得到如下声明:
@Inject
private CityAutocompleteDataBean<ArrayList<String>>;
@Inject
private WrParamsDataBean<HashMap<String, WrData>>;
相对于
@Inject
private CityAutocompleteDataBean;
@Inject
private WrParamsDataBean;
使用 <ArrayList<String>>
和 <HashMap<String, WrData>>
的原因大多数开发人员在看到这样的代码行时都会逃避。我也觉得它很丑,考虑到我们从哪里开始以及我们用它做什么。
尽管如此,这是我要求的,谢谢 Jesper。
您可以使用以下语法进行操作:
public abstract class AbstractCachedSupport<T extends Serializable> {
// ...
}
public class CachedMap<T extends Map<String, Integer> & Serializable>
extends AbstractCachedSupport<T> {
// ...
}
这意味着类型 T
必须实现 Map<String, Integer>
以及 Serializable
.
然后您可以将 CachedMap
与实现 Serializable
:
Map
实现一起使用
CachedMap<HashMap<String, Integer>> cachedMap = new CachedMap<>();