【问题标题】:Same erasure on for subclass that maps parent method to a different class将父方法映射到不同类的子类的相同擦除
【发布时间】:2019-05-18 01:29:49
【问题描述】:

我正在尝试重构 DAO,使其在我们的代码库中更有用。我们目前有一个参数化的 AbstractDao,它接受三种类型:

  1. 数据库表
  2. 数据库pojo
  3. 2) 的不同映射 pojo 表示

所以它最终看起来像:

public class AbstractDao<T extends DatabaseTable, R extends DatabaseRecord, M> {
  public AbstractDao(Connection connection, Mapper<R,M> mapper) {
  //save connection and mapper to protected variables
}
public List<M> insert(List<M> records) {
 connection.insertBulk(
   StreamEx.of(records).map(mapper::map).toList()
 );
 }
}

但是,这不适用于我们只处理 pojo 和 table 的经典 DAO 案例。

但是,这里有一个通用功能可以抽象为更基本的 AbstractDao,它在项目中很有用。比如:

AbstractDao<T extends DatabaseTable, R extends Record>

有一个子类

AbstractMappedDao<T extends DatabaseTable, R extends Record, M> extends AbstractDao<T, R>

摘要有一个类似的方法:

public List<R> insert(List<R> records) {
  connection.insertBulk(records);
}

并且 Mapped 应该有一个类似的方法:

public List<M> insert(List<M> records) {
  super.insert(StreamEx.of(records).map(mapper::map).toList());
}

但是,这会产生“相同的擦除”问题,因为 insert 接受泛型列表。

我已经尝试将它抽象成一个接口:

public interface Dao<T> {
  public List<T> insert(List<T> records);
}

让 Abstract 实现 Dao 和 Mapped 实现 Dao,但同样的问题。

所以我的问题是如何最好地解决这个问题?如果我将地图的签名更改为以下内容,这将按预期工作:

insertMapped(List<M> mapped);

但我更愿意保持合同不变。

感谢您的帮助。期待讨论!

【问题讨论】:

    标签: java algorithm generics design-patterns collections


    【解决方案1】:

    当涉及到组合行为时,最好还是使用组合而不是继承,这实际上是你的情况。 mapper 不会增强 已经存在于您的Dao 中的行为,就像添加 行为一样,在其中增加了一层间接性;这不一定是Dao 的问题,例如aspect/cross cutting concern

    所以,我的建议是创建一个能够组合 mappersAbstractDao 类(您可以根据需要只拥有一个;但是通过组合很容易让单个 Dao 对象支持多个映射器):

            private Map<Class, Function> mappers;
    
            public <M> void registerMapper(Class<M> mappingClass, Function<M, R> mapper) {
                mappers.put(mappingClass, mapper);
            }
    

    然后创建一个insert 方法,该方法允许使用它已注册的mappers 处理未扩展Record 的记录的预转换,如下所示:

            public <M> List<M> insert(List<M> records) {
                if (records.isEmpty()) return records;
                M rec = records.get(0);
    
                List<? extends Record> actualRecords = (rec instanceof Record) ? 
                        (List<Record>)records : createMappedRecords(records, rec.getClass());
    
                connection.insertBulk(actualRecords);
                return records;
            }
    

    这更简洁、更健壮和更可扩展,因为您的insert 可以通过组合的关注点以集中方式处理各种关注点。完整的编译代码如下所示:

    import java.util.List;
    import java.util.Map;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    
    public class ParentErasure {
    
        public abstract class AbstractDao<T extends DatabaseTable, R extends Record> {
            private Connection connection;
            private Map<Class, Function> mappers = new HashMap<>();
    
            public <M> void registerMapper(Class<M> mappingClass, Function<M, R> mapper) {
                mappers.put(mappingClass, mapper);
            }
    
            public <M> List<M> insert(List<M> records) {
                if (records.isEmpty()) return records;
                M rec = records.get(0);
    
                List<? extends Record> actualRecords = (rec instanceof Record) ? 
                        (List<Record>)records : createMappedRecords(records, rec.getClass());
    
                connection.insertBulk(actualRecords);
                return records;
            }
    
            private <M> List<R> createMappedRecords(List<M> records, Class<? extends Object> recordsClazz) {
                Function<M, R> mapper = mappers.get(recordsClazz);
                return records.stream()
                        .map(mapper::apply)
                        .collect(Collectors.toList());
            }
        }
    
        public interface Dao<T> {
            public List<T> insert(List<T> records);
        }
    }
    
    class Record {}
    class DatabaseTable {}
    class DatabaseRecord {}
    class Connection {
        public void insertBulk(List<? extends Record> records) {}
    }
    

    Complete code on GitHub

    希望这会有所帮助。

    【讨论】:

    • 太棒了。感谢您的详细解释。我认为这与我正在寻找的内容一致。整个“沉着于继承”的设计原则是我期待阅读的。绝对为我指明了正确的方向!
    • 在这里,您正在组合行为,例如将 lambdas 作为参数传递时。尽管通过 lambdas 你需要小心它的一些细微差别,例如我在以下答案中描述的那些:stackoverflow.com/questions/56149752/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多