Наследование Конструктора 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);
}
}
этот код является повторяющимся, а не сухим и бесполезным (ИМХО)... так что снова возникает вопрос:
почему java не поддерживает наследование конструктора? Есть ли выгода в том, чтобы не допустить этого наследования?
10 ответов:
предположим, конструкторы были по наследству... тогда потому, что каждый класс в конечном итоге происходит от объекта,каждый класс будет иметь конструктор без параметров. Это плохая идея. Что именно вы ожидаете:
FileInputStream stream = new FileInputStream();делать?
теперь потенциально должен быть способ легко создавать "сквозные" конструкторы, которые довольно распространены, но я не думаю, что это должно быть по умолчанию. Параметров, необходимых для построения подкласс часто отличается от тех, которые требуются суперклассу.
когда вы наследуете от супер-это то, что в реальности происходит:
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(); } }Итак, это причина, потому что вы должны вызвать свой уникальный конструктор, так как"супер" не имеет значения по умолчанию.
теперь, пытаясь угадать, почему Java не поддерживает наследование конструктора, вероятно, потому, что конструктор имеет смысл только в том случае, если он говорит о конкретных экземплярах, и вы не должны быть в состоянии создать экземпляр чего-то, когда вы не знаете, как он определен (by полиморфизм.)
потому что построение объекта подкласса может быть выполнено по-другому, чем построение суперкласса. Возможно, вы не хотите, чтобы клиенты подкласса могли вызывать определенные конструкторы, доступные в суперклассе.
глупый пример:
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's 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из этого примера вы видите, что вам нужно каким-то образом объявить, что "я хочу наследовать эти конструкторы" или "я хочу наследовать все конструкторы, кроме these", а затем вам также нужно будет указать предпочтение наследования конструктора по умолчанию на случай, если кто-то добавит новый конструктор в суперкласс... или вы можете просто потребовать, чтобы вы повторили конструкторы из суперкласса, если вы хотите "наследовать" их, что, возможно, является более очевидным способом сделать это.
потому что конструкторы-это деталь реализации - они не то, что пользователь интерфейса/суперкласса может фактически вызвать вообще. К тому времени, когда они получают экземпляр, он уже построен; и наоборот, в то время, когда вы создаете объект, по определению нет переменной, которой он в настоящее время назначен.
подумайте о том, что значит заставить все подклассы иметь унаследованный конструктор. Я утверждаю, что проще передать переменные напрямую, чем для класса "волшебно" есть конструктор с определенным количеством аргументов только потому, что это родитель.
конструкторы не являются полиморфными.
При работе с уже построенными классами вы можете иметь дело с объявленным типом объекта или любым из его подклассов. Вот для чего полезно наследование.
Конструктор всегда вызывается на определенный тип, напримерnew String(). Гипотетические подклассы не играют в этом никакой роли.
ответ Дэвида правильный. Я хотел бы добавить, что вы можете получить знак от Бога, что ваш дизайн испорчен, и что " сын "не должен быть подклассом" супер", но вместо этого у Супер есть некоторые детали реализации, лучше всего выраженные С функциональность, которую предоставляет сын, как своего рода стратегия.
EDIT: ответ Джона Скита является удивительным.
потому, что (супер)класс должен иметь полный контроль над тем, как он построен. Если программист решает, что не имеет смысла предоставлять конструктор по умолчанию (без args) как часть контракта класса, то компилятор не должен его предоставлять.
вы по существу наследуете констукторы в том смысле, что вы можете просто вызвать super, если и когда это уместно, просто это будет подвержено ошибкам по причинам, упомянутым другими, если это произошло по умолчанию. Компилятор не может предполагать, когда это уместно, а когда нет.
задача компилятора заключается в том, чтобы обеспечить как можно большую гибкость при одновременном снижении сложности и риска непреднамеренных побочных эффектов.
Я не знаю любой язык, где подклассы наследуют конструкторы (но тогда я не очень много программирую полиглот).
здесь обсуждение того же вопроса относительно C#. Общий консенсус, по-видимому, заключается в том, что это усложнит язык, введет потенциал для неприятных побочных эффектов к изменениям в базовом классе и, как правило, не должно быть необходимым в хорошем дизайне.
производный класс не является тем же самым классом, что и его базовый класс, и вам может быть все равно, инициализируются ли какие-либо члены базового класса во время построения производного класса. Это определение, сделанное программистом, а не компилятором.
Comments