Reputation: 1798
Consider the abstract class:
public abstract class Animal { ...}
and the interface:
public interface Canine<T extends Animal> {...}
I've defined the concrete classes:
public class Dog extends Animal implements Canine<Dog> {...}
public class Wolf extends Animal implements Canine<Wolf> {...}
I'd like to build a repository
class that accesses the database of animals and returns them. I've defined it in the following way:
public interface Repository {
Option<Dog> findById(String id, Class<Dog> type);
Option<Wolf> findById(String id, Class<Wolf> type);
(note: Option
is taken from the vavr
library)
This repository is used in the following class:
public abstract AbstractFinderClass<T extends Animal & Canine<T>> {
private Class<T> animalType;
public AbstractFinderClass(Class<T> animalType) {
this.animalType = animalType;
}
public Option<T> findMyAnimal(String id) {
return repository.findById(id, this.animalType);
}
}
which in turn is implemented in concrete form with:
public class DogFinder extends AbstractFinderClass<Dog> {
public DogFinder() {
super(Dog.class);
}
}
Now, the line return repository.findById(id, this.animalType)
causes two errors:
this.animalType
is of type Class<T>
while the expected type is Class<Dog>
, and these are apparently incompatible;Option<T>
while instead I get Option<Dog>
I am afraid I am missing some "little" detail, as I would expect Dog
and T
to be compatible.
Could you help me in fixing the problem?
Upvotes: 2
Views: 94
Reputation: 45329
The first problem is that you're having an unnecessary type parameter for DogFinder
. It's a dog finder, so a type parameter for what it finds is superfluous (the unconventionally named type parameter Dog
could perhaps have indicated a problem). It should be:
class DogFinder extends AbstractFinderClass<Dog> {
public DogFinder() {
super(Dog.class);
}
}
Second, your Repository
type has methods that are bound to specific types. This makes little sense because you want it to be generic. So you can use just one method, (optionally) making the repository itself generic (in the process solving the signature clash problem):
interface Repository<T extends Animal> {
Option<T> findById(String id, Class<T> type);
}
Third, unless we're missing context, I believe your Canine
type doesn't need to be generic (unless things must be convoluted):
interface Canine {
}
If you need a dedicated canine finder, you can simply change your repository class, like so:
abstract class CanineFinderClass<T extends Animal & Canine>
implements Repository<T> {...}
As a side note, the DogFinder
repository is redundant unless it offers special dog methods, like findAllPuppies()
. Otherwise, making AbstractFinderClass
concrete should be enough as the type is generic (just an example):
class AnimalFinderClass<T extends Animal> implements Repository<T> {
Repository<T> repository;
private Class<T> animalType;
public AbstractFinderClass(Class<T> animalType) {
this.animalType = animalType;
}
public Option<T> findMyAnimal(String id) {
return repository.findById(id, this.animalType);
}
}
Upvotes: 2