Java 用泛型类包装非泛型类的正确方法

Java 用泛型类包装非泛型类的正确方法,java,generics,Java,Generics,我试图在我的项目中包装(并简化)库,如下所示: public class MapDbPersistentStorage<V> { private DB db; private HTreeMap<String, V> storage; public MapDbPersistentStorage(String filename) { db = DBMaker .fileDB(filename)

我试图在我的项目中包装(并简化)库,如下所示:

public class MapDbPersistentStorage<V> { private DB db; private HTreeMap<String, V> storage; public MapDbPersistentStorage(String filename) { db = DBMaker .fileDB(filename) .fileMmapEnable() .make(); storage = (HTreeMap<String, V>) db .hashMap("section", Serializer.STRING, Serializer.LONG) .createOrOpen(); } public V get(String key, V defaultValue) { return storage.getOrDefault(key, defaultValue); } public void set(String key, V value) { storage.put(key, value); } public void close() { db.close(); } } 公共类MapDbPersistentStorage { 私人数据库; 私有HTreeMap存储; 公共MapDbPersistentStorage(字符串文件名) { db=DBMaker .fileDB(文件名) .fileMmapEnable() .make(); 存储=(HTreeMap)db .hashMap(“节”,Serializer.STRING,Serializer.LONG) .createOrOpen(); } 公共V get(字符串键,V defaultValue) { 返回storage.getOrDefault(key,defaultValue); } 公共无效集(字符串键,V值) { 存储。放置(键、值); } 公众假期结束() { db.close(); } }
正如已经指出的,问题在于
序列化程序.LONG
部分(应该是
V
类型)。我需要根据提供的泛型类型设计一个序列化程序。正确的方法是什么?

Java使用擦除来实现它的泛型。在运行时,您的类不知道您使用什么来实例化它。该信息不会在运行时保留

因此,如果您在运行时需要它,您必须手动保留它。但是在您当前的程序中,编译器不知道类型
Long
应该如何与
Serailizer.Long
关联

为了(某种程度上)类型安全,您可以将
序列化程序
包装在泛型类或接口中,该泛型类型对应于序列化程序编码的类型。有效地提供了连接两种类型系统的方法

interface GenericSerializer<T> extends Supplier<Serializer> {

    public static GenericSerializer<Long> ofLong() {
        return () -> Serializer.Long;
    }

    ...
}
接口GenericSerializer扩展了供应商{
LONG()的公共静态泛型序列化程序{
return()->Serializer.Long;
}
...
}
然后在你们班上:

public class MapDbPersistentStorage<V> {
    private DB db;
    private HTreeMap<String, V> storage;
    private final Serializer ser;

    public MapDbPersistentStorage(GenericSerializer<V> serFactory) {
        this.ser = serFactory.get();
    }

    public MapDbPersistentStorage(String filename) {
        db = DBMaker
                .fileDB(filename)
                .fileMmapEnable()
                .make();

        storage = (HTreeMap<String, V>) db
                .hashMap("section", Serializer.STRING, ser)
                .createOrOpen();
    }

    ...
}
公共类MapDbPersistentStorage{
私人数据库;
私有HTreeMap存储;
专用最终序列化程序;
公共MapDbPersistentStorage(GenericSerFactory序列化程序){
this.ser=serFactory.get();
}
公共MapDbPersistentStorage(字符串文件名){
db=DBMaker
.fileDB(文件名)
.fileMmapEnable()
.make();
存储=(HTreeMap)db
.hashMap(“节”,Serializer.STRING,ser)
.createOrOpen();
}
...
}
用法:

MapDbPersistentStorage<Long> m = new MapDbPersistentStorage<>(GenericSerializer.ofLong());
mapdpersistentstorage m=newmapdpersistentstorage(GenericSerializer.ofLong());

编译器可以检查
泛型序列化程序的类型是否与
MapDbPersistentStorage的类型相对应

序列化程序
传递给包装器的构造函数。这是否意味着我将传递
Long
类型两次?一次作为泛型参数,一次作为构造函数参数?您不认为这是一个坏主意,因为可以提供两个不兼容的类型吗?是的,但是您需要它两次,一次在编译时(泛型类型),一次在运行时(序列化程序
)。如果您想让它更安全,您可以将
序列化程序
包装在具有序列化程序泛型类型的泛型接口中,然后要求该接口作为参数。我强烈反对“我需要它两次”的说法!我的意思是Java可能会,但我需要它一次。我不确定你的解决方案是什么,你能写下你的解决方案吗?谢谢,但这是如何避免两次提供
Long
类型的呢?您仍然将其命名为泛型参数以及构造函数参数@Mehran你无法回避这个问题,答案解释了原因。至少这提供了一些编译时检查,您可以传入预期的
序列化程序
并试图在运行时处理其类型?@Mehran 1。)您必须传入实际的虚拟对象,该对象将具有对其类的引用。在运行时,字段的静态类型只是
对象
(由于擦除)。2.)您还必须以某种方式将该类型转换为相应的
序列化程序。