简体   繁体   English

将父方法映射到不同类的子类的相同擦除

[英]Same erasure on for subclass that maps parent method to a different class

I am attempting to refactor a DAO to make it a bit more usable in our code base.我正在尝试重构 DAO 以使其在我们的代码库中更有用。 We currently have a parameterized AbstractDao that takes in three types:我们目前有一个参数化的 AbstractDao,它接受三种类型:

  1. The database table数据库表
  2. The database pojo数据库pojo
  3. A different mapped pojo representation of 2) 2) 的不同映射 pojo 表示

So it ends up looking like:所以它最终看起来像:

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()
 );
 }
}

However, this doesn't work on the classic DAO case where we are dealing only with the pojo and the table.但是,这不适用于我们仅处理 pojo 和表的经典 DAO 案例。

However, there is a common functionality here that can be abstracted into a more basic AbstractDao that is useful across projects.然而,这里有一个通用的功能,可以抽象成一个更基本的 AbstractDao,它可以跨项目使用。 Something like:就像是:

AbstractDao<T extends DatabaseTable, R extends Record>

which has a subclass它有一个子类

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

The Abstract has a method like: Abstract 有一个方法,如:

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

and the Mapped should have a method like:并且 Mapped 应该有一个方法,如:

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

However, this gives a "same erasure" issue because insert takes in a List of generics.但是,这会产生“相同擦除”问题,因为 insert 接受泛型列表。

I have tried abstracting it out into an interface:我尝试将其抽象为一个接口:

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

And making Abstract implement Dao and Mapped implement Dao, but again, same issue.并使 Abstract 实现 Dao 和 Mapped 实现 Dao,但同样,同样的问题。

So my question is how to best approach this problem?所以我的问题是如何最好地解决这个问题? This works as expected if I change map's signature to something like:如果我将地图的签名更改为以下内容,这将按预期工作:

insertMapped(List<M> mapped);

But I would prefer to keep the contract the same.但我更愿意保持合同不变。

Thanks for the help.谢谢您的帮助。 Looking forward to the discussion!期待讨论!

When it comes down to composing behavior it is always best to either use composition over inheritance, and this is actually your case.当涉及到组合行为时,最好使用组合而不是继承,这实际上就是您的情况。 The mapper does not augment behavior already existing in your Dao as much as adds behavior, an extra layer of indirection in it; mapper不会像增加行为那样增加你的Dao已经存在的行为,这是一个额外的间接层; which is not necessarily a concern of the Dao , like an aspect/cross cutting concern .这不一定是Dao的关注点,就像 方面/横切关注点一样

So, my recommendation is to create a single AbstractDao class with the ability to compose mappers (you can have just one as you wanted; but with composition is easy to allow a single Dao object to support multiple mappers):因此,我的建议是创建一个具有组合mappers能力的AbstractDao类(您可以根据需要只拥有一个;但是通过组合很容易让单个Dao对象支持多个映射器):

        private Map<Class, Function> mappers;

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

Then create an insert method that allows for handling the pre-transformation of the records that do not extend Record using the mappers that it has registered, like this:然后创建一个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;
        }

This is cleaner, more robust and more extensible, since your insert can be made to entertain all sorts of concerns in a centralized manner with composed concerns .这是更干净、更健壮和更可扩展的,因为您的insert可以通过组合关注以集中方式处理各种关注 The full compiling code would look like something like this:完整的编译代码如下所示:

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 GitHub 上的完整代码

Hope this helps.希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM