Java:How to override this generic method?

12,639

Solution 1

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

Solution 2

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

Solution 3

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

Solution 4

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 A defines the method with the original signature. class B implements it and chooses List<Integer> for type parameter T. And finally, class C uses this method with an Iterable whore generic type is a subclass of List<Integer>.

Solution 5

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.

Your approach

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

is not a correct override:

  • since Java >=1.5 allows covariant return 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.
Share:
12,639
beginner_
Author by

beginner_

Updated on June 05, 2022

Comments

  • beginner_
    beginner_ almost 2 years
    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.

  • beginner_
    beginner_ over 11 years
    I'm not declaring the method I override. It's an existing class from Spring Data. See edit.
  • beginner_
    beginner_ over 11 years
    I get that. But I do not need the <S extends T>. T is a fixed Type, eg. always the same type, and T is a final class. Besides that depending on input I'm creating a new Object of MyType in the method. Mytype newObject = new MyType(args); That does not work with S.
  • Andreas Dolk
    Andreas Dolk over 11 years
    Confused. If a final class is used for T then you won't find a class for S which makes the method quite unusable (for this case)
  • DaveFar
    DaveFar over 11 years
    IC, adopted my answer accordingly.
  • beginner_
    beginner_ over 11 years
    Doesn't T fulfill S? I'm confused now too. :O
  • beginner_
    beginner_ over 11 years
    Sorry. I was wrong. I wanted the class to be final but it is an Entity and the can't be final.
  • asgs
    asgs over 6 years
    No offence, but this is not a solution to your problem you have stated in the question, hence not the "best" answer either. what you've ended up doing is a work around