扩展接口时返回子元素列表

时间:2014-02-10 18:02:20

标签: java generics overloading generic-list

我正在开发一个库,当我将返回类型重写为子元素列表时,我遇到了一个问题。

最初看起来像这样,

    Class Student {
    }

    public interface A {
        public Student isCurrentStudent(String name);
        public List<Student> getStudentList();
    }

    public interface B extends A {
        public List<Student> getColleges();
        public Student getBestFriend();
    }

然后我想介绍用于访问SeniourStudent的SeniorStudent类和API,所以我提出了以下内容,

Class SeniorStudent extends Student{
}

public interface C extends A {
    public SeniorStudent isCurrentStudent(String name);
    public List<SeniorStudent> getStudentList();
    public void addStudent(Attributes s);
}

public interface D extends B,C {
    public List<SeniorStudent> getColleges();
    public Student getBestFriend();
}

问题从接口C和D开始。由于java支持协变返回类型,因此C的第一个方法不会产生错误。但是当返回类型为“List”时,就会出现问题。所以我更改了上面的接口以使用泛型。

public interface A <T extends Student> {
    public Student isCurrentStudent(String name);
    public List<T> getStudentList();
}

public interface B<T extends Student> extends A<T> {
    public List<T> getColleges();
    public Student getBestFriend();
}

public interface C extends A<SeniorStudent> {
    public SeniorStudent isCurrentStudent(String name);
    public List<SeniorStudent> getStudentList();
    public void addStudent(Attributes s);
}

public interface D extends B<SeniorStudent>,C {
    public List<SeniorStudent> getColleges();
    public SeniorStudent getBestFriend();
}

我相信这个设计可以改进,这样B类的实现者可以限制只使用Student类。

最终我想要的是,提供可以像这样使用的干净API,

B bObject = getBObject();
List<Student> studentList = bObject.getColleges();

D dObject = getDObject();
List<SeniourStudent> seniorStudentList = dObject.getColleges();

如果有一种方法在子元素列表中覆盖返回类型,整个问题就会解决。

由于我的实际代码要大得多,我在这里用简单的例子来描述问题。希望它足够清楚。


修改

为B引入BS超类型可以解决问题。但这是更清洁的方式吗?

class diagram when introducing SB

public interface A <T extends Student> {
    public Student isCurrentStudent(String name);
    public List<T> getStudentList();
}

public interface BS<T extends Student> extends A<T> {
    public List<T> getColleges();
    public Student getBestFriend();
}

public interface B extends BS<Student> {
    public List<Student> getColleges();
    public Student getBestFriend();
}

public interface C extends A<SeniorStudent> {
    public SeniorStudent isCurrentStudent(String name);
    public List<SeniorStudent> getStudentList();
    public void addStudent(Attributes s);
}

public interface D extends BS<SeniorStudent>,C {
    public List<SeniorStudent> getColleges();
    public SeniorStudent getBestFriend();
}

谢谢,

2 个答案:

答案 0 :(得分:1)

我有点困惑你正在努力做什么。但我猜,您的问题可以通过不使接口CD通用来解决。你可以像这样使用你的界面:

interface A<T extends Student> {
    public T isCurrentStudent(String name);
    public List<T> getStudentList();
}

interface B<T extends Student> extends A<T> {
    public List<T> getColleges();
}

interface C extends B<Student> {
    public Student isCurrentStudent(String name);
    public List<Student> getStudentList();
    public List<Student> getColleges();
}

interface D extends B<SeniorStudent> {
    public SeniorStudent isCurrentStudent(String name);
    public List<SeniorStudent> getStudentList();
    public List<SeniorStudent> getColleges();
}

然后,分别为CD创建类实现接口StudentSeniorStudent。这是我能想到的最好方法。看看你是否可以相处,否则我们可以尝试进一步即兴。

答案 1 :(得分:1)

正如PlínioPantaleão在评论中所说(或实际上想要说),您可以定义界面以返回List<? extends Student>

import java.util.List;

class Attributes {}
class Student { }

interface A {
    Student isCurrentStudent(String name);
    List<? extends Student> getStudentList();
}

interface B extends A {
    List<? extends Student> getColleges();
}


class SeniorStudent extends Student{ }

interface C extends A {

    @Override
    SeniorStudent isCurrentStudent(String name);

    @Override
    List<SeniorStudent> getStudentList();

    void addStudent(Attributes s);
}

interface D extends B,C {
    @Override
    List<SeniorStudent> getColleges();
}
public class CovariantList
{
    public static void main(String[] args)
    {
        B b = null;
        List<? extends Student> collegesB = b.getColleges();

        D d = null;
        List<SeniorStudent> collegesD = d.getColleges();
    }
}

如果您只知道最“基本”的界面(在本例中为B),则只能获得List<? extends Student>。如果您知道特定类型(如D中所示),则可以获得协变类型List<SeniorStudent>


编辑根据评论和已修改的问题进行更新:

可以通过以下方式实现API在主要问题的编辑部分中使用的要求:

import java.util.List;

public class StudentsReturnTypesTest
{
    public static void main(String[] args)
    {
        B bObject = getBObject();
        List<Student> studentList = bObject.getColleges();

        D dObject = getDObject();
        List<SeniorStudent> seniorStudentList = dObject.getColleges();        
    }

    private static D getDObject() { return null; }
    private static B getBObject() { return null; }
}

interface A<T extends Student> {
    public Student isCurrentStudent(String name);
    public List<T> getStudentList();
}

interface AS extends A<Student> { }

interface B extends AS {
    public List<Student> getColleges();
}

interface C extends A<SeniorStudent> {
    public SeniorStudent isCurrentStudent(String name);
    public List<SeniorStudent> getStudentList();
    public void addStudent(Attributes s);
}

interface D extends C /* B Not possible, see below */  {
    public List<SeniorStudent> getColleges();
}

interface Attributes {}
interface Student {}
interface SeniorStudent extends Student {}

然而在其中一条评论中,您提到了

“对于接口D我也希望从B继承方法”

这很难。您基本上遇到的问题是,方法getColleagues()将以不同的返回类型继承:一次使用List<Student>,一次使用List<SeniorStudent>。这样做根本不是类型安全的。

目前,接口B中定义的唯一方法是getColleagues()。并且由于此方法(具有不同的返回类型)已经从接口C继承,我假设“... 从B继承方法”的意图引用了当前未包含的方法您发布的示例代码。

对此的解决方案可能是将“B方法”提取到专用接口中。这里的关键是避免继承具有不同返回类型的方法两次。这可以像在这个例子中那样实现,其中各个方法总结在一个名为AdditionalMethodsThatHaveBeenInB的界面中......

import java.util.List;

public class StudentsReturnTypesTest
{
    public static void main(String[] args)
    {
        B bObject = getBObject();
        List<Student> studentList = bObject.getColleges();
        bObject.methodThatWasInB();

        D dObject = getDObject();
        List<SeniorStudent> seniorStudentList = dObject.getColleges();
        dObject.methodThatWasInB();
    }

    private static D getDObject() { return null; }
    private static B getBObject() { return null; }
}

interface A<T extends Student> {
    public Student isCurrentStudent(String name);
    public List<T> getStudentList();
}

interface AS extends A<Student> { }

interface B extends AS, AdditionalMethodsThatHaveBeenInB {
    public List<Student> getColleges();
}

interface AdditionalMethodsThatHaveBeenInB {
    void methodThatWasInB();
}

interface C extends A<SeniorStudent> {
    public SeniorStudent isCurrentStudent(String name);
    public List<SeniorStudent> getStudentList();
    public void addStudent(Attributes s);
}

interface D extends C, AdditionalMethodsThatHaveBeenInB {
    public List<SeniorStudent> getColleges();
}

interface Attributes {}
interface Student {}
interface SeniorStudent extends Student {}

当然,很难说你的真正的代码是否也可以这样做,但也许类似的方法适用于那里。