如何在继承类中更改集合类型?

时间:2018-07-16 10:14:36

标签: java inheritance

我有一个基类,它定义了一个集合List<Person>。现在,一个扩展基类的特定类应该更改集合类型

有可能吗?

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 {

    }
}

3 个答案:

答案 0 :(得分:1)

否,不可能。您无法覆盖字段,只能将其隐藏。

处理这种情况的正确方法是将Person声明为公共接口。 Request的子类可以使用Person的不同实现,但是调用代码可能不必关心具体类型。

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()  { /* ... */ }
}

答案 1 :(得分:1)

子类不能重写方法并更改返回类型。允许子类在人员列表中发送不同类型的元素的方法有两种。

这两个都假设SpecialPersonPerson的子类:

1-使Request类具有通用性:

public class Request<P extends Person> {
    private List<P> persons;

    public List<P> getPersons() {
       //...
    }
}

这样,SubRequest3将声明为:

public class SubRequest3 extends Request<SubRequest3.Person> {
    private List<SubRequest3.Person> persons;
}

所有其他SubRequest类将声明extends Request<Person>

2-将所有内容保持在API级别,但将SubRequest3.Person的实例添加到列表中,而不是尝试返回某些List<SubRequest3.Person>。这利用了Person/SubRequest3.Person的继承,同时避免了与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());
}

答案 2 :(得分:1)

您只需声明您的类即可接受T的通用类型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;
    }
}

然后让您的子类定义应使用的类型。例如:

public class SubRequest3 extends Request<SubRequest3.Person>{ /* ... */ }

您可能已经看到people列表是最终列表,这意味着它将永远不会被覆盖。与仅接受任何List实现相比,此尝试要安全得多,因为您可以确保该实现能够真正实现您想要的功能。例如。无法通过setPeople传递有害列表。

answer from @Michael类似,您可能还想声明一个默认实现,该实现将T声明为Person,然后让SubRequest1SubRequest2扩展该默认类。