【问题标题】:The right way to wrap a non-generic class with a generic one用泛型类包装非泛型类的正确方法
【发布时间】:2017-07-14 11:02:54
【问题描述】:

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

公共类 MapDbPersistentStorage { 私人数据库; 私有 HTreeMap 存储; 公共 MapDbPersistentStorage(字符串文件名) { db = 数据库制造商 .fileDB(文件名) .fileMmapEnable() 。制作(); 存储 = (HTreeMap) 数据库 .hashMap("section", Serializer.STRING, Serializer.LONG) .createOrOpen(); } 公共 V 获取(字符串键,V 默认值) { 返回 storage.getOrDefault(key, defaultValue); } 公共无效集(字符串键,V值) { storage.put(键,值); } 公共无效关闭() { db.close(); } }

正如已经指出的那样,问题出在Serializer.LONG 部分(它应该是V 类型)。我需要根据提供的泛型类型提出一个序列化程序。这样做的正确方法是什么?

【问题讨论】:

  • Serializer 传递给包装器的构造函数。
  • 那不是说我将传递Long 类型两次吗?一次作为泛型参数,一次作为构造函数参数?你不认为这是一个坏主意,因为一个可以提供两种不兼容的类型?
  • 是的,但是您需要两次,一次在编译时(泛型类型),一次在运行时(Serializer)。如果你想让它更安全,你可以将Serializer 包装在具有序列化器泛型类型的泛型接口中,然后将该接口作为参数。
  • 我强烈反对“我需要两次”的说法!我的意思是 Java 可能,但 需要它一次。您能否写下您的解决方案,因为我不确定它是什么?

标签: java generics


【解决方案1】:

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

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

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

interface GenericSerializer<T> extends Supplier<Serializer> {

    public static GenericSerializer<Long> ofLong() {
        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<Long> m = new MapDbPersistentStorage<>(GenericSerializer.ofLong());

编译器可以检查GenericSerializer的类型是否与MapDbPersistentStorage的类型相对应。

【讨论】:

  • 谢谢,但是这如何消除了提供两次Long 类型?您仍然将其命名为泛型参数以及构造函数参数!
  • @Mehran 你无法解决这个问题,答案解释了原因。至少这提供了一些编译时检查,您通过了预期的Serializer
  • 定义一个private V dummy; 并尝试在运行时处理它的类型怎么样?
  • @Mehran 1.) 您必须传入实际的虚拟对象,该对象将引用它的类。在运行时,该字段的静态类型只是Object(由于擦除)。 2.) 您还必须以某种方式将该类型转换为相应的Serializer
猜你喜欢
  • 2021-02-16
  • 1970-01-01
  • 1970-01-01
  • 2015-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-01
  • 1970-01-01
相关资源
最近更新 更多