Java 处理某些抽象类的子类的重载方法

Java 处理某些抽象类的子类的重载方法,java,Java,我有一个通用的Person类,还有两种类型的人Student和Teacher,它们扩展了Person类。我也有教学会议,其中将存储一份学生名单和一份教师名单,将在该会议 class Person {} class Student extends Person {} class Teacher extends Person {} class Session { List<Student> students = new ArrayList(); List<Teacher&

我有一个通用的
Person
类,还有两种类型的人
Student
Teacher
,它们扩展了
Person
类。我也有教学会议,其中将存储一份学生名单和一份教师名单,将在该会议

class Person {}
class Student extends Person {}
class Teacher extends Person {}

class Session {
  List<Student> students = new ArrayList();
  List<Teacher> teachers = new ArrayList();

  // add a person to the teaching session.
  // They should be placed into the appropriate lists
  public void enroll(Person p)
  {
    if (p instanceof Student)
      students.add((Student) p)
    else if (p instanceof Teacher)
      teachers.add((Teacher) p)
  }
}
但是,遍历
Person
对象列表的代码似乎需要确定当前的Person是学生还是教师实例,并在将其传递给
enroll
方法之前适当地键入cast


有没有一种方法可以让我设计它,这样我就不需要在我自己的代码中的任何时间点调用
instanceof

您可以使用每个具体
实现的重载方法来做您建议的事情,除此之外,还可以在列表中添加一个类型:

List<Student> students = new ArrayList<>();
List<Teacher> teachers = new ArrayList<>();
List students=new ArrayList();
列表教师=新建ArrayList();

然后,在迭代逻辑中,您就知道了类型。

您可以改变它:

class Session {
  StudentList students = new StudentList();
  TeacherList teachers = new TeacherList();

  // add a person to the teaching session.
  // They should be placed into the appropriate lists
  public void enroll(Person p)
  {
     p.addMe(students, teachers);
  }
}

public class StudentList extends ArrayList<Student> {
}

public class TeacherList extends ArrayList<Teacher> {
}

public abstract class Person {
    public abstract void addMe(StudentList sList, TeacherList tList);
}

public class Student extends Person {
    public void addMe(StudentList sList, TeacherList tList) {
        sList.add(this);
    }
}

public class Teacher extends Person {
    public void addMe(StudentList sList, TeacherList tList) {
        tList.add(this);
    }
}
课堂会话{
StudentList students=new StudentList();
教师列表教师=新教师列表();
//将一个人添加到教学会话中。
//应将其列入适当的清单
公开作废登记(p人)
{
p、 addMe(学生、教师);
}
}
公共类学生列表扩展了ArrayList{
}
公共类教师列表扩展了ArrayList{
}
公共抽象类人物{
公共摘要无效地址(学生列表、教师列表);
}
公营班级学生人数{
public void addMe(学生列表、教师列表){
添加(这个);
}
}
公立班主任延伸人{
public void addMe(学生列表、教师列表){
t添加(此项);
}
}

最简单的方法是向person类添加一个
isTeacher()
方法

这是一个基本的实现(您已经编写的部分省略了)

如果您还想避免类型转换,请尝试此

abstract class Person {
  public abstract Teacher asTeacher();
  public abstract Student asStudent();
}

class Student extends Person {
  public Student asStudent() { return this; }
  public Teacher asTeacher() { return null; }
}

class Teacher extends Person {
  public Student asStudent() { return null; }
  public Teacher asTeacher() { return this; }
}

class Session extends Person {
  public void enroll(Person p) {
    Teacher t = p.asTeacher();
    if (t != null) teachers.add(t);
    else students.add(p.asStudent());
  }
}
这种实现有一个小缺点,即它假设教师和学生是唯一的人,但将其扩展到多种类型是相当简单的

  • 会话
    需要一个重载的注册方法,如您的问题所示
  • 抽象注册
    方法添加到以
    会话
    为参数的
    Person
    类中

    公开摘要无效注册(会话s)

  • 教师
    学生
    每个覆盖
    注册

    public void enroll (Session s) {
         s.enroll(this);
    }
    

  • p instanceof Student
    的问题是,您根据非方法调用的内容更改行为,因此将来的更改将很困难(即,如果您添加另一个子类Person)

    有几种方法可以解决这个设计问题:

    • 为什么需要创建人员的层次结构?在你的系统中,一个学生有可能成为另一门课程的老师吗?如果是这样的话,你有一些关于一个人的信息,而这个人在课程中的角色会发生什么变化。因此,使用继承是不方便的,为此,使用组合并创建一个对象来表示
      在课程中的角色

    • 您可以添加
      isTeacher
      方法,或使用访问者模式拆分集合。这将比
    实例稍微好一点。但在我看来,问题仍然在于不正确的类层次结构

    例如,使用“角色”:

    正如您所看到的,为所有人使用
    isTeacher
    方法看起来很尴尬。角色解决方案更好地反映了作为学生或教师的时间属性


    你可以使用访问者,它将把你从
    中丑陋的
    isTeacher
    中解救出来;但对我来说,这个解决方案过于复杂,您将把“丑陋”转化为
    访问者
    界面。

    只是为了完整起见,这不是一个特别好的方法,但我应该提到的是,为了添加和捕获抛出的
    ClassCastException
    而强制转换它。假设学生比老师多,你会这么做,所以老师是抛出例外的人。在这里使用泛型有什么害处?是的,我相应地更新了示例,以反映学生列表将只包含
    学生
    实例,而教师列表将只包含
    教师
    实例的想法。我将研究访问者模式,看看如何在这种情况下应用它。您尝试过吗?我在你发布答案之前就这么做了,但没有成功。否则,我强烈建议您发布支持您建议答案的代码。尝试了什么?正在实例化新列表()?将其更改为具体的ArrayList:)尝试使用重载方法并使用这些方法添加
    列表的元素。顺便说一下,
    列表
    将包含
    学生
    教师
    对象引用实例。这就是问题所在,在我的解决方案中没有列表。顺便说一下,我在问题中没有看到一个明确的列表。这个想法是其他一些代码将有一个人员列表,并根据需要迭代该列表以将他们注册到适当的会话中。。。看起来像是遍历Person对象列表的代码。我有一个理由。这让整个事情都顺利进行。为什么你认为它不需要呢?顺便说一下,你的代码甚至没有编译,并且显示了一个糟糕的设计。请注意,如果你有两个以上的类扩展这个
    Person
    类,你将不得不向这个
    addMe
    方法添加更多的参数…@luigimendoza足够公平了。虽然在我看来,它展示了一种解决问题的技巧
    abstract class Person {
      public abstract Teacher asTeacher();
      public abstract Student asStudent();
    }
    
    class Student extends Person {
      public Student asStudent() { return this; }
      public Teacher asTeacher() { return null; }
    }
    
    class Teacher extends Person {
      public Student asStudent() { return null; }
      public Teacher asTeacher() { return this; }
    }
    
    class Session extends Person {
      public void enroll(Person p) {
        Teacher t = p.asTeacher();
        if (t != null) teachers.add(t);
        else students.add(p.asStudent());
      }
    }
    
    public void enroll (Session s) {
         s.enroll(this);
    }
    
    enum Role { TEACHER, STUDENT; }
    
    class Session {
       List<SessionEnrolment> enrollments = new ArrayList<>();
    
       public void enroll(Person p, Role role)
       {
           enrollments.add(new SessionEnrollment(p, role));
       }
    
       public List<Person> getTeachers() {
          List<Person> result = new ArrayList<>();
          for (SessionEnrolment e : enrollments) {
                if (e.isTeacher()) { result.add(e.getPerson()); }
                return result;
          }
       }
    }
    
    public void enroll(Person p)
    {
        if (p.isTeacher()) { teachers.add(p); } else { students.add(p); }
    }