Java:如何覆盖这个通用方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13049420/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 11:19:44  来源:igfitidea点击:

Java:How to override this generic method?

javagenericsoverridingtype-erasure

提问by beginner_

public <S extends T> List<S> save(Iterable<S> entities) {
        //...
}

If I use following method to override

如果我使用以下方法来覆盖

@Override
public List<MyType> save(Iterable<MyType> structures) {
    List<MyType> result = new ArrayList<>();
    //...
    return result;
}

I get following error:

我收到以下错误:

method does not override or implement a method from a supertype

name clash: save(Iterable<MyType>) in MyTypeRepositoryImpl and <S>save(Iterable<S>) in SimpleJpaRepository have the same erasure, yet neither overrides the other
  where S,T are type-variables:
    S extends T declared in method <S>save(Iterable<S>)
    T extends Object declared in class SimpleJpaRepository

How can I solve this? I don't need the method to be generic and in fact it should not be. What I mean is that

我该如何解决这个问题?我不需要该方法是通用的,事实上它不应该是通用的。我的意思是

@Override
public <S extends MyType> List<S> save(Iterable<S> structures) {
    List<S> result = new ArrayList<>();
    //...
    return result;
}

Will not work as the method can create a new Object of MyType which is not "compatible" to List.

将不起作用,因为该方法可以创建与 List 不“兼容”的 MyType 的新对象。

How can I make this work?

我怎样才能使这项工作?

EDIT:

编辑:

For clarification. I'm trying to override the different save() methods of Spring data SimpleJpaRepository (which is extented by QuerydslJpaRepository)

为了澄清。我正在尝试覆盖 Spring 数据 SimpleJpaRepository 的不同 save() 方法(由 QuerydslJpaRepository 扩展)

Class defintions:

类定义:

public class MyTypeRepositoryImpl
    extends QueryDslJpaRepository<MyType, Long>
    implements MyTypeRepository

@NoRepositoryBean
public interface MyTypeRepository
    extends JpaRepository<MyType, Long>,
    QueryDslPredicateExecutor<MyType> 

And this (from Spring Data)

这(来自 Spring Data)

public class QueryDslJpaRepository<T, ID extends Serializable> 
extends SimpleJpaRepository<T, ID> 
implements QueryDslPredicateExecutor<T>

EDIT 2:

编辑2:

The method calls save(MyType entity) for each element and that method contains following logic:

该方法为每个元素调用 save(MyType entity) ,该方法包含以下逻辑:

  1. entity has a field which is unique
  2. get that fields value and check if entity with that value already exists
  3. if yes, use that entity (call to entityManager.merge) -> does not work returns MyType not S
  4. if no create a new one -> here new object is created. Does not work with generic type
  1. 实体有一个唯一的字段
  2. 获取该字段值并检查具有该值的实体是否已存在
  3. 如果是,请使用该实体(调用 entityManager.merge)-> 不起作用返回 MyType 而不是 S
  4. 如果没有创建一个新的 -> 这里创建了新的对象。不适用于泛型类型

For 4. I can just set id = null and use the passed in object. That does not work for 3.

对于 4. 我可以设置 id = null 并使用传入的对象。这对 3 不起作用。

So I'm very puzzled why this method has this signature. It makes it unusable for me and i don't get why I would save a subclass of T using Ts DAO. the save methods are the only ones with . All others just use T. I could just cast to S to make it compile but that seems ugly too...as any other type than T would lead to an exception.

所以我很疑惑为什么这个方法有这个签名。它使我无法使用它,我不明白为什么要使用 Ts DAO 保存 T 的子类。save 方法是唯一带有 . 所有其他人都只使用 T。我可以将其强制转换为 S 以使其编译,但这看起来也很丑陋……因为除 T 之外的任何其他类型都会导致异常。

采纳答案by beginner_

My solution was to not override this at all but to create a service class that does the needed logic and leave repository untouched.

我的解决方案是根本不覆盖它,而是创建一个服务类来执行所需的逻辑并保持存储库不变。

回答by John B

For one method to override another it must apply to at least all valid parameters of the overridden method. Your base method is generic public <S extends T> List<S> save(Iterable<S> entities). So it will accept any type Sthat extends T. However your override is more restrictive because it will only accept collections of MyType, therefore it is not a valid override.

一个方法要覆盖另一个方法,它必须至少适用于被覆盖方法的所有有效参数。您的基本方法是通用的public <S extends T> List<S> save(Iterable<S> entities)。所以它会接受任何S扩展的类型T。但是,您的覆盖更具限制性,因为它只接受 的集合MyType,因此它不是有效的覆盖。

If you had your base class defined with T, and the method accepted just T, and the derived class locked down Tto MyTypeyou should be OK.

如果你用 定义了你的基类T,并且方法只接受T,并且派生类锁定TMyType你应该没问题。

To give a better answer we need to see the class declarations for the two classes. I would suggest the following:

为了给出更好的答案,我们需要查看两个类的类声明。我建议如下:

class MyClass<T>{
  public List<T> save(Iterable<T> entities);
}

class OtherClass extends MyClass<MyType>{
  public List<MyType> save(Iterable<MyType> entities);
}

EDIT:

编辑:

If you don't have control over the base class (which it seems that you don't), you are stuck with the public <S extends MyType> List<S> save(Iterable<S> structures)signature. This is because the overridden method is genericized and so the overridding method must also be

如果您无法控制基类(您似乎没有控制权),那么您就会被public <S extends MyType> List<S> save(Iterable<S> structures)签名所困扰。这是因为覆盖的方法是泛化的,所以覆盖的方法也必须是

回答by Walter Laan

Depends on how you have defined , the following works

取决于您如何定义,以下工作

public class TestGenerics2<T> {
    public <S extends T> List<S> save(Iterable<S> entities) {
        return new ArrayList<S>();
    }
}

public class TestGenerics3 extends TestGenerics2<Number> {
    @Override
    public <S extends Number> List<S> save(Iterable<S> entities) {
        return super.save(entities);
    }
}

回答by Andreas Dolk

Here's an example that compiles and that shows how to use it:

这是一个编译示例并展示了如何使用它:

abstract class A<T> {
    abstract public <S extends T> List<S> save(Iterable<S> entities); 
}

class B extends A<List<Integer>> {

    @Override
    public <S extends List<Integer>> List<S> save(Iterable<S> entities) {
        return null;
    }
}

class C {
    public void useIt() {
        B b = new B();
        Iterable<ArrayList<Integer>> it = new ArrayList<ArrayList<Integer>>();
        b.save(it);
    }
}

Class Adefines the method with the original signature. class Bimplements it and chooses List<Integer>for type parameter T. And finally, class Cuses this method with an Iterablewhore generic type is a subclass of List<Integer>.

A定义具有原始签名的方法。类B实现它并选择List<Integer>类型参数T。最后, classC使用此方法与一个Iterable妓女泛型类型是List<Integer>.

回答by DaveFar

Since

自从

public <S extends T> List<S> save(Iterable<S> entities)

is given, your override must be for

给出,您的覆盖必须用于

public <S extends MyType> List<S> save(Iterable<S> structures)

and your implementation must respect that S might be a real subtype of MyType.

并且您的实现必须尊重 S 可能是 MyType 的真正子类型。

Your approach

你的方法

public List<MyType> save(Iterable<MyType> structures)

is not a correct override:

不是正确的覆盖:

  • since Java >=1.5 allows covariantreturn types and List<MyType>is a subtype of List<S extends MyType>, this is ok, but
  • Java only allows invariant parameter types, but Iterable<MyType>is a subtype of Iterable<S extends MyType>, hence your compiler error message.
  • 由于 Java >=1.5 允许协变返回类型并且List<MyType>是 的子类型List<S extends MyType>,因此可以,但是
  • Java 只允许不变的参数类型,但它Iterable<MyType>是 的子类型Iterable<S extends MyType>,因此您的编译器错误消息。