Наследование конструктора Java

Мне было интересно, почему в java-конструкторах не наследуются? Вы знаете, когда у вас есть такой класс:

public class Super {

  public Super(ServiceA serviceA, ServiceB serviceB, ServiceC serviceC){
    this.serviceA = serviceA;
    //etc
  } 

}

Позже, когда вы наследуете от Super, java будет жаловаться на то, что конструктор по умолчанию не определен. Решение, очевидно, что-то вроде:

public class Son extends Super{

  public Son(ServiceA serviceA, ServiceB serviceB, ServiceC serviceC){
    super(serviceA,serviceB,serviceC);
  }

}

Этот код повторяется, а не DRY и бесполезен (IMHO)... так что он снова задает вопрос:

Почему Java не поддерживает наследование конструктора? Есть ли какая-либо польза в том, что вы не разрешаете это наследование?

Ответ 1

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

FileInputStream stream = new FileInputStream();

сделать?

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

Ответ 2

Когда вы наследуете Super, это происходит на самом деле:

public class Son extends Super{

  // If you dont declare a constructor of any type, adefault one will appear.
  public Son(){
    // If you dont call any other constructor in the first line a call to super() will be placed instead.
    super();
  }

}

Итак, это причина, потому что вам нужно вызвать ваш уникальный конструктор, так как "Super" не имеет значения по умолчанию.

Теперь, пытаясь угадать, почему Java не поддерживает наследование конструктора, возможно, потому, что конструктор имеет смысл только в том случае, если он говорит о конкретных экземплярах, и вы не сможете создать экземпляр чего-либо, когда вы не знаете как он определяется (полиморфизмом).

Ответ 3

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

Глупый пример:

class Super {
    protected final Number value;
    public Super(Number value){
        this.value = value;
    }
}

class Sub {
    public Sub(){ super(Integer.valueOf(0)); }
    void doSomeStuff(){
        // We know this.value is an Integer, so it safe to cast.
        doSomethingWithAnInteger((Integer)this.value);
    }
}

// Client code:
Sub s = new Sub(Long.valueOf(666L)): // Devilish invocation of Super constructor!
s.doSomeStuff(); // throws ClassCastException

Или даже проще:

class Super {
    private final String msg;
    Super(String msg){
        if (msg == null) throw new NullPointerException();
        this.msg = msg;
    }
}
class Sub {
    private final String detail;
    Sub(String msg, String detail){
        super(msg);
        if (detail == null) throw new NullPointerException();
        this.detail = detail;
    }
    void print(){
        // detail is never null, so this method won't fail
        System.out.println(detail.concat(": ").concat(msg));
    }
}
// Client code:
Sub s = new Sub("message"); // Calling Super constructor - detail is never initialized!
s.print(); // throws NullPointerException

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

Ответ 4

Поскольку конструкторы представляют собой детали реализации - они не являются тем, что пользователь интерфейса/суперкласса может вообще вызывать. К тому моменту, когда они получают экземпляр, он уже построен; и наоборот, в то время, когда вы создаете объект там по определению, нет переменной, которой он в настоящее время назначен.

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

Ответ 5

Ответ Дэвида правильный. Я хотел бы добавить, что вы можете получить знак от Бога, что ваш дизайн испорчен, и что "Сын" не должен быть подклассом "Супер", но вместо этого у Super есть некоторые особенности реализации, наиболее выраженные благодаря функциональности, которую предоставляет Сын, в качестве стратегии.

РЕДАКТИРОВАТЬ: Ответ на вопрос Джона Скита является самым замечательным.

Ответ 6

Конструкторы не являются полиморфными.
При работе с уже построенными классами вы можете иметь дело с объявленным типом объекта или любым его подклассом. То, что наследование полезно.
Конструктор всегда вызывается для конкретного типа, например new String(). Гипотетические подклассы в этом не играют никакой роли.

Ответ 7

Не один убедительный ответ.

Каждая проблема, с которой вы (обладаете) с наследованием конструктора уже имеете с любым другим методом. Если у вас есть ошибка в вашем унаследованном классе, чем есть ошибка. Драма...

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

Ответ 8

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

Ответ 9

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

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

Ответ 10

Я не знаю какого-либо языка, где подклассы наследуют конструкторы (но тогда я не много программируемый полиглотт).

Ниже приведено обсуждение того же вопроса о С#. Общий консенсус, по-видимому, заключается в том, что это усложнит язык, представит потенциал для неприятных побочных эффектов для изменений в базовом классе и вообще не будет необходимо в хорошем дизайне.

Ответ 11

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