Java:How to override this generic method?

beginner_ picture beginner_ · Oct 24, 2012 · Viewed 12.2k times · Source
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.

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)

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)

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

EDIT 2:

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

  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

For 4. I can just set id = null and use the passed in object. That does not work for 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.

Answer

John B picture John B · Oct 24, 2012

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 S that extends T. However your override is more restrictive because it will only accept collections of MyType, therefore it is not a valid override.

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

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