Reputation: 86935
I have a base class that defines a collection List<Person>
. Now, one specific class extending the base class should change the collection type.
Is that possible?
public class Request {
private List<Person> persons;
//getter, setter
}
public class SubRequest1 extends Request {
}
public class SubRequest2 extends Request {
}
//only this class should define a different list type
public class SubRequest3 {
private List<SubRequest3.Person> persons;
//compiler error. this is invalid code!
@Override
public List<SubRequest3.Person> getPersons() {
return persons;
}
@Override
public void setPersons(List<SubRequest3.person> persons) {
this.persons = persons;
}
static class SubRequest3.Person extends Person {
}
}
Upvotes: 0
Views: 287
Reputation: 19910
You can just declare your class to accept a generic type T
which extends Person
:
public class Request<T extends Person> {
private final List<T> people = new ArrayList<>(); // or any other implementation
public void setPeople(List<? extends T> people){
this.people.clear();
this.people.addAll(people);
}
public List<T> getPeople(){
return people;
}
}
And then let your subclasses define what type should be used. E.g:
public class SubRequest3 extends Request<SubRequest3.Person>{ /* ... */ }
You may have seen that the people
list is final, which means it will never be overriden. This attempt is a lot more safer than just accepting any List
implementation, because you can ensure that the implementation does what you actually want. E.g. no harmful List can be passed via setPeople
.
Like in the answer from @Michael you might aswell want to declare a Default implementation which declares T
to just be Person
and then let SubRequest1
and SubRequest2
extend that default class.
Upvotes: 1
Reputation: 45339
The subclass cannot override a method and change the return type. There are two possible ways to allow the subclass send a different type of element in the person list.
Both of these suppose that SpecialPerson
is a subclass of Person
:
1 - Make the Request
class generic:
public class Request<P extends Person> {
private List<P> persons;
public List<P> getPersons() {
//...
}
}
With that, SubRequest3
will be declared as:
public class SubRequest3 extends Request<SubRequest3.Person> {
private List<SubRequest3.Person> persons;
}
All other SubRequest
classes will declare extends Request<Person>
2 - Keep everything at API level as is, but add instances of SubRequest3.Person
to a list, rather than trying to return some List<SubRequest3.Person>
. This takes advantage of the Person/SubRequest3.Person
inheritance, while avoiding the issue of invariance associated with List<Person>
/List<SubRequest3.Person>
private List<SubRequest3.Person> persons;
public void setPersons(List<Person> people) {
//cast element by element
this.persons.clear(); //or whatever means of resetting
people.stream().forEach(p -> persons.add((SubRequest3.Person) p));
}
public List<SubRequest3.Person> getPersons() {
//Can also use a loop and add manually to a `List<Person>` object
return this.persons.stream().map(p -> (Person) p).collect(Collectors.toList());
}
Upvotes: 1
Reputation: 44250
No, it's not possible. You can't override fields, you can only hide them.
The correct way to handle this situation is to declare Person
as a common interface. The subclasses of Request
can use different implementations of Person
, but the calling code probably shouldn't care about the concrete type.
interface Person
{
//...
}
interface Request<T extends Person>
{
void setPeople(List<T> person);
List<T> getPeople();
}
abstract class DefaultRequest implements Request<Person>
{
private List<Person> persons;
public void setPeople(List<Person> people) { /* ... */ }
public List<Person> getPeople() { /* ... */ }
}
class SubRequest1 extends DefaultRequest
{
//...
}
class SubRequest2 extends DefaultRequest
{
//...
}
class SubRequest3 implements Request<SpecialPerson>
{
private List<SpecialPerson> persons;
public void setPeople(List<SpecialPerson> people) { /* ... */ }
public List<SpecialPerson> getPeople() { /* ... */ }
}
Upvotes: 1