Проверьте, является ли вызывающий объект экземпляром дочернего класса - PullRequest
4 голосов
/ 23 июля 2011

У меня есть 2 класса. Давайте назовем их классом A и классом B. Класс A содержит метод, который выполняет некоторое действие. Класс B переопределяет этот метод своей собственной версией, но делает super вызов этого метода в классе A для выполнения действия. Прямо сейчас это работает нормально. Однако в классе A есть некоторые действия, которые должны выполняться только в том случае, если объект является only экземпляром класса A. Иными словами, некоторые действия в методе класса A не должны выполняться, если объект является экземпляр ребенка класса А.

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

Есть ли хорошие альтернативы, или instanceof путь?

public class A{

    public void someMethod(){

        // Only perform these actions if it is not a child class.  This is what
        // I am looking for a better solution for
        if(!(this instanceof B)){
            // Some action...
        }

        // Actions to execute for every call
    }
}

public class B extends A{

   @Override
   public void someMethod(){

       super.someMethod();

       // More actions
   }
}

Так же, как объяснение дизайна, я использую его для генерации XML. В программе у меня есть List<A> для хранения данных. Когда пришло время вывести XML, я перебираю список и вызываю generateXML (someMethod занимает свое место в моем примере).

При создании объекта класса A его данные должны быть в тегах <A></A>. При создании объекта класса B его данные должны быть в тегах <B></B>. Но все свойства A также должны быть внутри тегов <B></B>, поэтому на данный момент он вызывает тот же метод generateXML, который используется, когда объект имеет только класс A

Но, как отмечали некоторые другие, вызов того же метода не является подходящим способом. Класс B должен вызывать защищенный метод в классе A, который генерирует только необходимую информацию.

Ответы [ 8 ]

7 голосов
/ 23 июля 2011

Создайте защищенные методы, которые делают специфичные для класса вещи, и вызывайте их из someMethod ().Класс A предоставит свою реализацию, и если подкласс должен эффективно удалить этот код, он может переопределить защищенный метод пустой реализацией.

Не бороться с полиморфизмом;используйте его в своих интересах.

Пример:

public class A {
    protected void someOtherMethod() {
        // Do stuff specific to A here.
    }

    public void someMethod() {
        // Do some stuff

        someOtherMethod();

        // Do some more stuff
    }
}

public class B extends A {
    @Override
    protected void someOtherMethod() {
        // Empty implementation; B doesn't need to do this.
        // Or do stuff specific to B...
    }
}
3 голосов
/ 23 июля 2011

Что ж, теперь это ужасный дизайн, и его нужно решать по-другому (например, сделать часть, вызываемую из подкласса собственным защищенным методом), но, увы, если вам это действительно нужно, вы можете проверить на

this.getClass () == A.class, чтобы увидеть, действительно ли класс идентичен.

1 голос
/ 23 июля 2011

Это может быть тем же, что пытался сказать cdhowie.(Без обид: я думал, что вы немного неясны.)

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

Ваш пример не дает никаких подробностей о том, что вы пытаетесь сделать, поэтому я просто что-то придумаю.

!!!Неправильный путь !!!

public class Car
{
  public void goUphill()
  {
    // XYZ has automatic transmission -- don't need to downshift
    if (!(this instanceof XYZ())
    {
      downshift();
    }
    pressGas();
  }
}

public class ModelXYZCar extends Car
{
  public void goUphill()
  {
    tellWifeToGetOutAndPush();
    super.goUphill();
  }
}

Лучший способ:

public class Car
{
  public boolean isManualTransmission()
  {
    // default
    return true;
  }
  public void goUphill()
  {
    if (isManualTransmission())
    {
      downshift();
    }
    pressGas();
  }
}

public class ModelXYZCar extends Car
{
  public boolean isManualTransmission()
  {
    return false;
  }
  public void goUphill()
  {
    tellWifeToGetOutAndPush();
    super.goUphill();
  }
}

Таким образом, суперкласс не должен знать, что нужно подклассам.Каждый подкласс определяет свое собственное поведение.В этом случае каждый подкласс определяет функцию isManualTransmission и возвращает true или false, в зависимости от ситуации.

Еще лучше избежать необходимости использования флагов и задать соответствующее поведение в каждом классе:

abstract public class Car
{
  abstract public void downshift();
  public void goUphill()
  {
    downshift();
    pressGas();
  }
}

public class AutomaticTransmissionCar extends Car
{
  public void downshift()
  {
    // Automatic transmission -- no need to do anything
  }
}
public class ManualTransmissionCar extends Car
{
  public void downshift()
  {
    ... whatever ...
  }
}
public class ModelXYZCar extends ManualTransmissionCar
{
  public void goUphill()
  {
    tellWifeToGetOutAndPush();
    super.goUphill();
  }
}
1 голос
/ 23 июля 2011

Почему бы и нет:

public class A{

    public void someMethod(){

    // Only perform these actions if it is not a child class.  This is what
    // I am looking for a better solution for
        if(this.getClass().equals(A.class))
        // Some action...
        }

    // Actions to execute for every call
    }
}

И

public class B extends A{

    @Override
   public void someMethod(){

       super.someMethod();

       // More actions
   }
}
1 голос
/ 23 июля 2011

Я вижу instanceof как возможность использовать полиморфизм.

Сохранить поведение в someMethod, характерное для всех типов A.Подкласс A для реализации поведения, которое вы сейчас проверяете с помощью instanecof.

public class A{

    public void someMethod(){

        // Get rid of the special section and put it in it's own class
        // Keep only agnostic behavior common to all types of A
        // Actions to execute for every call
    }
}

public class B extends A{

   @Override
   public void someMethod(){

       super.someMethod();

       // More actions
   }
}

public class C extends A{

   @Override
   public void someMethod(){
       super.someMethod();

       // Actions that were originally in the if(instanceof) check
   }
}
0 голосов
/ 23 июля 2011

"вызов супер" - это анти-паттерн .Вместо того, чтобы ожидать, что подкласс вызовет функциональность суперкласса, переверните его наоборот.Теперь суперкласс полностью контролирует то, что называется как до, так и после «обычного».

public class A {
    public void someMethod() {
        beforeCommonStuff();
        // Actions to execute for every call
        afterCommonStuff();
    }

    protected void beforeCommonStuff() {
        // Actions to execute only for class A
    }

    protected void afterCommonStuff() {}
}

public class B extends A {
    @Override
    protected void afterCommonStuff(){
        // More actions
    }
}
0 голосов
/ 23 июля 2011

Я могу ошибаться, но ваш случай кажется идеальным сценарием "отдавать предпочтение композиции перед наследованием" и "инкапсулировать то, что меняется".Один общий порядок действий в ситуации, подобной вашей, заключается в следующем:

  1. Не допускайте, чтобы ваши подклассы выходили за пределы суперкласса
  2. Изолируйте часть кода, которая должна принадлежать только А, в частномМетод.
  3. Создайте экземпляр A в B, C и т. д. и вызовите A.someMethod ().

    public class A {
        public void someMethod(){}
        private void someOtherMethod(){
            // move behavior specific to A in here.
        }       
    }
    
    public class B { // no extends
        public void someMethod() {
            new A().someMethod();
        }
    }
    

Опять же, я мог бы бытьнеправильно, и это может не относиться к вашему делу.

0 голосов
/ 23 июля 2011

Выражение A.class.equals(this.getClass()) возвращает true, только если это действительно экземпляр класса A. Если это дочерний экземпляр, результат будет ложным.

НО: если вам нужно сделать это, проверьте свой дизайн.Звучит очень не объектно-ориентированный стиль.Я могу дать вам альтернативу.Разделите ваш базовый класс A на 2: действительно базовый класс A0 и его дочерний элемент A1.Ваш класс B будет братом A1 и будет расширяться непосредственно от A0.

В этом случае вы можете поместить общий код в класс A0 и весь конкретный код (который в вашей версии должен выполняться только в том случае, если классточно A, а не его подкласс) к A1.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...