与构造函数的通用一对多关系

Generic one-to-many relationship with constructor

我有一个使用通用一对多关系的实体。不幸的是,lobmok @AllArgsConstructor,无法 map/cast 一个 List 扩展到 List。 EducationTranslation 和 Translatable 的属性完全相同,EducationTranslation extends Translatable。 如果我删除我的自定义构造函数,我会收到以下错误:

Can not set java.lang.Long field EducationTranslation.id to Translatable

是否有注释或设计模式可以让我摆脱自己编写的构造函数并使代码更简洁更短?

@OneToMany(
    targetEntity = EducationTranslation.class,
    cascade = CascadeType.ALL,
    fetch = FetchType.LAZY)
private List<? extends Translatable> translationList = new ArrayList<>();


public Education(Long id, Profile profile, Collection<? extends Translatable> translationList) {
    this.id = id;
    this.profile = profile;
    this.translationList = translationList
        .stream()
        .map(x -> new EducationTranslation(x.getId(), x.getCode(), x.getTranslation()))
        .collect(Collectors.toList());
}

使用通配符限制的集合...对于 JPA 实体来说有些不方便。

请注意,在初始创建之后,您将永远无法在不进行转换的情况下将任何内容插入声明为 List<? extends Translatable> 的列表中,这意味着以下代码将不起作用:

Education education = entityManager.find(Education.class, id);
education.getTranslationList().add(new EducationTranslation(...)); // compilation error

之所以这样,是因为 List<? extends Translatable> 被解释为 'list of some unknown element type that extends Translatable',并且编译器不允许您的代码向这样的列表添加任何内容,不知道 exact type ? 代表。我想你会希望 .add(new EducationTranslation()) 是合法的,因为 EducationTranslation 确实 扩展了 Translatable 但这里更好地说明了为什么它不能可能被允许:

List<Dog> dogs = new ArrayList<>(List.of(new Dog())); //just a mutable collection with a Dog inside
List<? extends Animal> someAnimals = dogs; //a list of Dogs is a list of 'a type that extends Animal', so - valid assignment
someAnimals.add(new Cat()); //hey, a Cat does extend Animal, so this should be legal, right?
Dog shapeshifter = dogs.get(1); //...right???

综上所述,直接将translationList声明为List<EducationTranslation>会方便很多。

why I am allowed to initially add elements and save it the date store, but then get a compile error when trying to add an element using the same object signature

您没有使用 'the same signature'。您的代码 不是 在任何时候调用 .add()。它只是构造一个有效类型为List<EducationTranslation>的List,然后将其赋值给一个声明为List<? extends Translatable>的字段。问题是,您不能使用存储在该字段中的引用调用 .add(new EducationTranslation()),因为 字段的声明方式 ,而不是 您分配给它的内容。这与以下代码段中的场景几乎相同:

Object obj = "Hello";
obj.length(); // you know obj points to a String, but you assign it to a reference with a more general type, so this call is illegal

注意,如果要对Educationclass的client隐藏列表元素的具体类型,那么下面的代码是仍然合法:

interface TranslationHolder {

    List<? extends Translatable> getTranslationList();
}

class Education implements TranslationHolder {

    private List<EducationTranslation> translationList = new ArrayList<>();

    //this getter is a valid implementation for TranslationHolder.getTranslationList()
    public List<EducationTranslation> getTranslationList() {
        return translationList;
    }
}

class ClassThatUsesEducationButOnlyKnowsItAsTranslationHolder  {

   public void execute() {
        TranslationHolder holder = getEducationAsTranslationHolderInSomeIndirectWay();
        holder.getTranslationList(); // this still return a reference to List<? extends Translatable>, NOT List<EducationTranslation>
   }
}

因此,如果重点是从 Education class 的 客户端 抽象出 EducationTranslation,则不需要将字段本身声明为通配符列表。