Java 对将父方法映射到不同类的子类执行相同的擦除

Java 对将父方法映射到不同类的子类执行相同的擦除,java,algorithm,generics,design-patterns,collections,Java,Algorithm,Generics,Design Patterns,Collections,我试图重构DAO,使其在我们的代码库中更可用。我们目前有一个参数化的AbstractDao,它有三种类型: 数据库表 数据库pojo 2)的不同映射pojo表示形式 所以它最终看起来像: public class AbstractDao<T extends DatabaseTable, R extends DatabaseRecord, M> { public AbstractDao(Connection connection, Mapper<R,M> mapper)

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

  • 数据库表
  • 数据库pojo
  • 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()
     );
     }
    }
    
    公共类抽象DAO{
    公共抽象DAO(连接,映射器映射器){
    //将连接和映射程序保存到受保护的变量
    }
    公共列表插入(列表记录){
    connection.insertBulk(
    StreamEx.of(records).map(mapper::map).toList()
    );
    }
    }
    
    然而,在经典的DAO案例中,我们只处理pojo和表,这不起作用

    然而,这里有一个通用的功能,可以抽象为一个更基本的抽象DAO,它在各个项目中都很有用。比如:

    AbstractDao<T extends DatabaseTable, R extends Record>
    
    insertMapped(List<M> mapped);
    
    AbstractDao
    
    它有一个子类

    AbstractMappedDao<T extends DatabaseTable, R extends Record, M> extends AbstractDao<T, R>
    
    AbstractMappedDao扩展了AbstractDao
    
    摘要的方法如下:

    public List<R> insert(List<R> records) {
      connection.insertBulk(records);
    }
    
    公共列表插入(列表记录){
    连接.插入批量(记录);
    }
    
    映射的方法应类似于:

    public List<M> insert(List<M> records) {
      super.insert(StreamEx.of(records).map(mapper::map).toList());
    }
    
    公共列表插入(列表记录){
    super.insert(streamx.of(records).map(mapper::map.toList());
    }
    
    然而,这会产生一个“相同的擦除”问题,因为insert包含一个泛型列表

    我尝试将其抽象为一个接口:

    public interface Dao<T> {
      public List<T> insert(List<T> records);
    }
    
    公共接口Dao{
    公共列表插入(列表记录);
    }
    
    使抽象的实现Dao和映射的实现Dao,但同样的问题

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

    AbstractDao<T extends DatabaseTable, R extends Record>
    
    insertMapped(List<M> mapped);
    
    insertMapped(列表映射);
    
    但我宁愿保持合同不变

    谢谢你的帮助。期待讨论

    当涉及到组合行为时,最好要么使用组合,要么使用继承,这实际上就是你的情况。
    mapper
    不会增加
    Dao
    中已经存在的行为,而是在其中添加了一层额外的间接行为;这不一定是
    Dao
    所关心的问题,比如

    因此,我的建议是创建一个
    AbstractDao
    类,该类能够组合
    mapper
    (您可以根据需要只创建一个;但是通过组合,可以很容易地允许单个
    Dao
    对象支持多个映射器):

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

            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;
            }
    
    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) {}
    }
    
    import java.util.List;
    导入java.util.Map;
    导入java.util.function.function;
    导入java.util.stream.collector;
    公共类删除{
    公共抽象类AbstractDao{
    专用连接;
    私有映射器=新HashMap();
    public void registerMapper(类映射类、函数映射器){
    put(mappingClass,mapper);
    }
    公共列表插入(列表记录){
    if(records.isEmpty())返回记录;
    M rec=records.get(0);
    
    太棒了。谢谢你的详细解释。我想这正是我想要的。整个“继承之上的沉着”设计原则是我期待阅读的一个原则。它明确地为我指明了正确的方向!在这里,当你将lambdas作为参数传递时,你会做出这样的行为。尽管传递lambdas时,你需要注意它的一些细微差别,如我在下面的回答中描述的: