Как я могу заставить конструктор быть определенным во всех подклассах моего абстрактного класса - PullRequest
63 голосов
/ 02 июля 2010

У меня есть абстрактный класс A, который определяет абстрактные методы.Это означает, что для того, чтобы класс был инстанцируемым, все абстрактные методы должны быть реализованы.

Я бы хотел, чтобы все мои подклассы реализовали конструктор с 2-мя int как параметрами.

Объявление конструктора противоречит моей цели, так как я хочу, чтобы конструктор был определен в подклассах, и я ничего не знаю о реализации.Более того, я не могу объявить конструктор как абстрактный;

Есть ли способ сделать это?

Пример того, что я хочу:

Давайте скажемчто я определяю API класса Matrix.В моей задаче Matrix не может изменить свои размеры.

Для создания матрицы мне нужно указать ее размер.

Следовательно, я хочу, чтобы все мои разработчики предоставили конструктору размерв качестве параметра.Этот конструктор мотивирован проблемой, а не проблемой реализации.Реализация может делать с ними все, что захочет, при условии сохранения всей семантики методов.

Допустим, я хочу предоставить базовую реализацию метода invert() в моем абстрактном классе.Этот метод создаст новую матрицу с this инвертированными размерами.Более конкретно, как он определен в абстрактном классе, он создаст новый экземпляр того же класса, что и this, используя конструктор, который принимает два целых числа.Поскольку он не знает экземпляр, он будет использовать отражение (getDefinedConstructor), и я хочу получить способ гарантировать, что я его получу и что он будет иметь значение для реализации.

Ответы [ 5 ]

53 голосов
/ 02 июля 2010

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

Более того, как вы говорите, вы ничего не знаете о реализации - так откуда вы знаете, что для них уместно иметь конструктор, который требует двух целых чисел?Что если одному из них тоже нужна строка?Или, возможно, имеет смысл использовать константу для одного из этих целых чисел.

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

Один из вариантов - иметь отдельный интерфейс дляфабрика:

interface MyClassFactory
{
    MyClass newInstance(int x, int y);
}

Тогда каждому из ваших конкретных подклассов MyClass также понадобится фабрика, которая знает, как построить экземпляр с двумя целыми числами.Хотя это не очень удобно - и вам все равно придется создавать экземпляры самих фабрик.Опять же, какова реальная ситуация здесь?

6 голосов
/ 02 июля 2010

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

Это глупо.Сравните OK и Bad.Оба класса одинаковы, за исключением того, что OK отвечает вашим требованиям и, таким образом, проходит проверки во время выполнения.Таким образом, соблюдение этого требования способствует непродуктивной занятой работе.

Лучшим решением будет своего рода Фабрика.

abstract class RequiresConstructor
{
    RequiresConstructor( int x, int y ) throws NoSuchMethodException
    {
    super();
    System.out.println( this.getClass().getName() ) ;
    this.getClass(). getConstructor ( int.class , int.class ) ;
    }

    public static void main( String[] args ) throws NoSuchMethodException
    {
    Good good = new Good ( 0, 0 );
    OK ok = new OK ();
    Bad bad = new Bad ();
    }
}

class Good extends RequiresConstructor
{
    public Good( int x, int y ) throws NoSuchMethodException
    {
    super( x, y ) ;
    }
}

class OK extends RequiresConstructor
{
    public OK( int x, int y ) throws NoSuchMethodException
    {
    super( x, y ) ;
    throw new NoSuchMethodException() ;
    }

    public OK() throws NoSuchMethodException
    {
    super( 0, 0 ) ;
    }
}

class Bad extends RequiresConstructor
{
    public Bad() throws NoSuchMethodException
    {
    super( 0, 0 ) ;
    }
}
3 голосов
/ 08 июня 2016

Немного поздно, но ...

Просто создайте конструктор по умолчанию в вашем классе, который всегда называется супер-конструктором. В этом конструкторе по умолчанию вы можете проверить все определенные конструкторы с отражением на своем собственном объекте класса (который тогда является не абстрактным суперклассом, а конкретным подклассом). Если конструктор, который вы хотите реализовать, отсутствует, выведите исключение времени выполнения.

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

Посмотрите на этот пример:

import java.lang.reflect.Constructor;

public abstract class Gaga {
  public Gaga() {
    boolean found = false;
    try {
      Constructor<?>[] constructors = getClass().getConstructors();
      for (Constructor<?> c : constructors) {
        if (c.getParameterTypes().length==2) {
          Class<?> class0 = c.getParameterTypes()[0];
          Class<?> class1 = c.getParameterTypes()[1];
          if ( (class0.getName().equals("int") || class0.isAssignableFrom(Integer.class))
              &&  (class1.getName().equals("int") || class1.isAssignableFrom(Integer.class)) )
            found = true;
        }
      }
    } catch (SecurityException e)
    {
      found = false;
    }

    if (!found)
      throw new RuntimeException("Each subclass of Gaga has to implement a constructor with two integers as parameter.");

    //...
  }

}

И тестовый класс:

public class Test {
  private class Gaga1 extends Gaga {
    public Gaga1() { this(0, 0); }
    public Gaga1(int x, Integer y) { }
  }

  private class Gaga2 extends Gaga {

  }

  public static void main(String[] args)
  {
    new Gaga1();
    new Gaga1(1, 5);
    new Gaga2();
    System.exit(0);
  }
}

В основной функции будут созданы объекты Gaga1, но создание Gaga2 вызовет исключение времени выполнения.

Но вы не можете быть уверены, что этот конструктор вызван - вы даже не можете гарантировать, что он делает то, что вам нужно.

Этот тест полезен, только если вы работаете с отражением.

3 голосов
/ 02 июля 2010

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

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

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

0 голосов
/ 05 мая 2015

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

public abstract void setSize(int rows,int columns);
...