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

时间:2012-10-24 12:40:06

标签: java generics override type-erasure

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

如果我使用以下方法覆盖

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

我收到以下错误:

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

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

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

无法正常工作,因为该方法可以创建一个与List“不兼容”的新MyType对象。

我该如何做到这一点?

编辑:

澄清。我试图覆盖Spring数据SimpleJpaRepository的不同save()方法(由QuerydslJpaRepository扩展)

课程定义:

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

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

这(来自Spring Data)

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

编辑2:

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

  1. entity有一个唯一的字段
  2. 获取该字段值并检查具有该值的实体是否已存在
  3. 如果是,请使用该实体(调用entityManager.merge) - &gt;不起作用返回MyType而不是S
  4. 如果没有创建新的 - >这里创建了新对象。不适用于通用类型
  5. 对于4.我可以设置id = null并使用传入的对象。这不适合3。

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

5 个答案:

答案 0 :(得分:9)

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

如果您的基类定义为MyType,并且方法仅接受T,并且派生类已锁定TT,那么您应该没问题。

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

MyType

编辑:

如果您无法控制基类(您似乎没有),那么您将无法使用class MyClass<T>{ public List<T> save(Iterable<T> entities); } class OtherClass extends MyClass<MyType>{ public List<MyType> save(Iterable<MyType> entities); } 签名。这是因为重写的方法是通用的,因此重写方法也必须是

答案 1 :(得分:3)

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

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

答案 2 :(得分:2)

这是一个编译并显示如何使用它的示例:

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

A使用原始签名定义方法。 class B实现它,并为类型参数List<Integer>选择T。最后,类C使用此方法,Iterable妓女泛型类型是List<Integer>的子类。

答案 3 :(得分:1)

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

,你的覆盖必须是

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

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

你的方法

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

不是正确的覆盖:

  • 因为Java&gt; = 1.5允许covariant返回类型而List<MyType>List<S extends MyType>的子类型,这没关系,但是
  • Java只允许不变的参数类型,但Iterable<MyType>Iterable<S extends MyType>的子类型,因此是您的编译器错误消息。

答案 4 :(得分:0)

我的解决方案是不要覆盖这一点,而是创建一个服务类来执行所需的逻辑并保持存储库不受影响。