Java, возможно ли «преобразовать» объект из подкласса в объект из суперкласса

#java #object #subclass #superclass

#java #объект #подкласс #суперкласс

Вопрос:

У меня есть два класса Student и Tutor. Tutor — это, по сути, студент (Tutor расширяет Student), у которого есть facultyID. Как только его контракт будет завершен, он вернется к тому, чтобы быть просто студентом. Итак, могу ли я каким-то образом преобразовать его обратно в его «предыдущий» список учеников?

Комментарии:

1. Возможно, вам вообще не следует использовать наследование для моделирования этих отношений. Основываясь на вашем кратком описании, вы можете рассмотреть возможность создания оболочки Tutor, которая берет ученика во время разработки. Затем, когда контракт будет завершен, вы можете просто позволить ссылке на оболочку выпасть из области видимости и продолжить работу с экземпляром Student напрямую.

2. не могли бы вы дать немного больше контекста для вашего вопроса? имхо, это действительно зависит от того, чего вы пытаетесь достичь. возможно boolean isTutor; , достаточно добавить участника или просто сохранить список студентов, которые также являются преподавателями. не нужно моделировать все с помощью шаблонов OO только потому, что вы можете это сделать.

3. @kritzikratzi Мне нравится ваш комментарий «Нет необходимости моделировать все с помощью OO… просто потому, что вы можете «. Не могу не согласиться!

Ответ №1:

То, что вы действительно хотите здесь сделать, это использовать композицию, а не наследование. Сохраните все ваши объекты как тип Student , а затем временно назначьте поведение a TutorRole , как это требуется для каждого экземпляра Student .

При таком дизайне ваш Student класс будет содержать свойство (переменную-член) типа TutorRole , которое вы можете добавлять или удалять во время выполнения. Добавление isTutor() метода позволит вам четко и лаконично определить, является ли ученик преподавателем во время выполнения.

TutorRole Класс будет инкапсулировать поведение (т.Е. Методы) Преподавателя.

 /*
 * The TutorRole can be set at runtime
 */
public class Student {

    private String facultyId;

    private TutorRole tutorRole = null;

    public boolean isTutor() {
        return !(tutorRole == null);
    }

    public void doTutorStuff() {
        if(isTutor()) {
            tutorRole.doTutorStuff();
        }
        else {
            throw new NotTutorException();
        }
    }

    public void setTutorRole(TutorRole tutorRole) {
        this.tutorRole = tutorRole;
    }
}

/*
 * Ideally this class should implement a generic interface, but I'll keep this simple
 */
public class TutorRole {

    public void doTutorStuff() {
        // implementation here
    }
}

/*
 * Now let's use our classes...
 */
Student st = new Student(); // not a tutor
st.setTutorRole(new TutorRole()); // now a tutor
if(st.isTutor()) {
    st.doTutorStuff();
}
st.setTutorRole(null); // not a tutor anymore
 

Альтернативный подход заключается в том, чтобы Tutor класс содержал ссылку на Student объект, но это зависит от того, как вы собираетесь взаимодействовать с объектами Student и Tutor, в зависимости от того, каким образом вы хотите это закодировать.

Ответ №2:

Я думаю, это кричит о сдерживании и интерфейсном программировании.

Как насчет этого:

 interface IStudent
{
  String getName();
  int getStudentId();
}

interface IFacultyMember
{
  int getFacultyId( );
}

class Student
  implements IStudent
{
  String name;
  int id;

  public String getName( ) { return name; }
  public int getStudentId( ) { return id; }
}

class Tutor
  implements IStudent, IFacultyMember
{
  Student student;
  int facultyId;

  public Tutor ( Student student, int facultyId )
  {
    this.student = student;
    this.facultyId = facultyId;
  }

  public String getName( ) { return student.getName( ); }
  public int getStudentId( ) { return student.getStudentId( ); }
  public int getFacultyId( ) { return facultyId; };
}
 

Таким образом, ваш ученик остается учеником, даже если он переходит на позицию преподавателя. По истечении срока действия Tutor вы просто создаете запись tutor.

Запись студента, с другой стороны, по-прежнему будет доступна в центральных службах.

Ответ №3:

Как только вы создаете экземпляр некоторого типа (например, Tutor ), это тип среды выполнения, который будет иметь экземпляр. Это больше нельзя изменить.

Некоторые альтернативы:

  • Дайте Student некоторый конструктор, который принимает другой Student экземпляр и копирует соответствующие поля. Так называемый конструктор копирования. Таким образом, вы могли бы легко создать новый Student экземпляр на основе вашего Tutor , а затем использовать его вместо этого.
  • Если важно сохранить фактический экземпляр, а не создавать какую-то копию (возможно, вы сохранили ссылки повсюду), было бы лучше отделить роль от класса. Создайте какой FacultyMember -нибудь класс или что-то Student в этом роде и Tutor превратитесь в роли. Затем вы можете изменить роль FacultyMember позже.

Ответ №4:

Вы можете использовать Tutor в качестве ученика, чтобы ваш код рассматривал его как такового во время компиляции, но объект останется Tutor, поэтому вызов любых переопределенных методов приведет к вызову версии класса Tutor.

Единственный способ выполнить «преобразование», которое вы ищете, — это создать новый объект Student и наделить его всеми свойствами, которые есть у Преподавателя.

Поскольку вы обнаруживаете, что вам нужно выполнить это преобразование, возможно, вам захочется переосмыслить структуру классов. Например, может быть, и ученики, и преподаватели действительно должны быть просто личностями, и каждый человек может играть роль Ученика или Учителя.

Комментарии:

1. Не могли бы вы уточнить это? Я не уверен, как это сделать?

2. @vedran: Если вы хотите отредактировать свой ответ, чтобы предоставить часть вашего текущего кода и более полное объяснение того, чего вы пытаетесь достичь, преобразуя человека из одного типа в другой, я могу дать вам лучшую обратную связь.

Ответ №5:

Вы могли бы просто написать метод, подобный TutorToStudent() on Tutor, и создать нового ученика у своего преподавателя. Если вы приведете к тому, что объект все еще будет преподавателем.

Вы также можете рассмотреть возможность отказа от наследования в этом случае и просто иметь флаг, указывающий, является ли этот ученик преподавателем.

Комментарии:

1. Но тогда, если бы у вас была коллекция учеников, она все равно содержала бы старого преподавателя, а не нового обычного ученика.

2. @glowcoder: Верно, но это справедливо для большинства решений, которые по-прежнему поддерживают иерархию наследования и пытаются каким-то образом превратить Преподавателя в ученика. Как и любое решение, оно не решает все случаи, и, очевидно, потребуется дополнительное кодирование для учета случаев, подобных упомянутым вами.

3. да, это справедливо для решений, которые поддерживают иерархию наследования. Но хорошей практикой является «предпочтение композиции перед наследованием». Гораздо лучше иметь Student роль и Tutor роль и просто иметь экземпляр (или список) роли (или ролей, если используется список).

Ответ №6:

Нет такого понятия, как «превратить» его обратно в ученика, он — наставник, а наставник уже ЯВЛЯЕТСЯ учеником.

Однако может быть некоторая ценность в обобщении доступа к этому объекту, так что вы используете только метод student . Для этого в Java есть несколько идиом.

1) Вы можете использовать API Student исключительно в своем коде, за исключением части Tutor.

2) У вас может быть метод «toStudent / toTutor» для ваших объектов, который позволяет им возвращать объекты, специфичные для роли.

// Мои предпочтения 3) Вы можете использовать интерфейсы. Пусть Tutor и Student будут интерфейсами и создают ваши объекты с использованием реализации интерфейса. Если вы ссылаетесь на объекты, используя минимальные интерфейсы, а не тяжеловесные шаблоны наследования, ваш код, вероятно, будет лучше масштабироваться в будущем.

Ответ №7:

  1. Вы можете создать нового ученика с точно такой же информацией, что и у Преподавателя, и отказаться от исходного объекта. Либо с помощью конструктора копирования Student(Student original) , либо Student toStudent() с помощью метода Это быстрый и грязный способ.
  2. Вместо того, чтобы заставлять Репетитора напрямую расширять ученика, превращая Tutor конкретные данные и поведение в Декоратора. Или еще лучше, сделайте и Tutor то, и другое, и Student декораторов People объекта, который включает в себя всех. Это один из правильных способов сделать это.
  3. Сделайте Tutor и Student в a enum Type и удерживайте это поле People , это проще, но менее расширяемо, чем вышеописанное, это действительно зависит от характера различий между преподавателем и учеником.

Ответ №8:

Вы не можете изменить тип объектов в Java. Вероятно, самый простой способ добиться вашего пути — это клонировать все параметры из Tutor в новый объект Student.