Ошибка Java: неявный суперконструктор не определен для конструктора по умолчанию

89

У меня есть простой код Java, который по своей структуре похож на этот:

abstract public class BaseClass {
    String someString;
    public BaseClass(String someString) {
        this.someString = someString;
    }
    abstract public String getName();
}

public class ACSubClass extends BaseClass {
    public ASubClass(String someString) {
        super(someString);
    }
    public String getName() {
        return "name value for ASubClass";
    }
}

У меня будет довольно много подклассов BaseClass, каждый из которых реализует getName()метод по-своему ( шаблон метода шаблона ).

Это работает хорошо, но мне не нравится наличие в подклассах избыточного конструктора. Это больше для набора текста, и его трудно поддерживать. Если бы мне пришлось изменить сигнатуру метода BaseClassконструктора, мне пришлось бы изменить все подклассы.

Когда я удаляю конструктор из подклассов, я получаю эту ошибку времени компиляции:

Implicit super constructor BaseClass() is undefined for default constructor. Must define an explicit constructor

Возможно ли то, что я пытаюсь сделать?

Джоэл
источник
1
Пожалуйста, оставьте «лишний» конструктор! Он поддерживает читаемость вашего кода, и все современные IDE могут создавать его автоматически, поэтому вам просто нужно ввести ярлык.
Андреас Долк
3
Перечитывая свой собственный вопрос год спустя, мне приходит в голову, что я мог бы удалить конструкторы (в том числе в базовом классе), как предложил matt b, а затем использовать статический фабричный метод для создания экземпляров.
Джоэл

Ответы:

146

Вы получаете эту ошибку, потому что класс, у которого нет конструктора, имеет конструктор по умолчанию , который не имеет аргументов и эквивалентен следующему коду:

public ACSubClass() {
    super();
}

Однако, поскольку ваш BaseClass объявляет конструктор (и, следовательно, не имеет конструктора по умолчанию, no-arg, который в противном случае предоставил бы компилятор), это незаконно - класс, расширяющий BaseClass, не может вызывать super(); потому что нет конструктора без аргументов в BaseClass.

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

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

Мэтт Би
источник
17
«Это, вероятно, немного противоречит интуиции, потому что вы можете подумать, что подкласс автоматически имеет любой конструктор, который имеет базовый класс». +1
Mr_and_Mrs_D 03
2
Ради потомков я предлагаю свое решение для будущих читателей: создать конструктор без аргументов, BaseClassно просто заставить его генерировать UnsupportedOperationExceptionили что-то в этом роде. Это не лучшее решение (оно ошибочно предполагает, что класс может поддерживать конструктор без аргументов), но это лучшее, что я могу придумать.
JMTyler
49

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

Например, если вы импортируете проект Java 1.7 в Eclipse и у вас нет правильной настройки 1.7, вы получите эту ошибку. Затем вы можете перейти к Project - Preference - Java - Compilerи switch to 1.6 or earlier; или перейдите к Window - Preferences - Java - Installed JREsи добавьте / исправьте вашу установку JRE 1.7.

MF.OX
источник
2
Только что получил эту ошибку без видимой причины в Eclipse. Затем я очистил рабочее пространство (меню «Проект» -> «Очистить ...»), и оно исчезло.
erickrf
7

Это возможно, но не так, как у вас.

Вам нужно добавить в базовый класс конструктор без аргументов, и все!

public abstract class A {
    private String name;
    public A(){
        this.name = getName();
    }
    public abstract String getName();


    public String toString(){
        return "simple class name: " + this.getClass().getSimpleName() + " name:\"" + this.name + "\"";
    }
}
class B extends A {
    public String getName(){
        return "my name is B";
    }
    public static void main( String [] args ) {
        System.out.println( new C() );
    }
}
class C extends A {
    public String getName() {
        return "Zee";
    }
}

Если вы не добавляете конструктор (любой) к классу, компилятор добавляет для вас конструктор по умолчанию no arg.

Когда defualt no arg вызывает super (); и поскольку у вас его нет в суперклассе, вы получите это сообщение об ошибке.

Это о самом вопросе.

Теперь, расширяя ответ:

Вы знаете, что создание подкласса (поведения) для указания другого значения (данных) не имеет смысла ?? !!! Я надеюсь, что да.

Если единственное, что меняется - это "имя", тогда достаточно одного параметризованного класса!

Так что вам это не нужно:

MyClass a = new A("A");
MyClass b = new B("B");
MyClass c = new C("C");
MyClass d = new D("D");

или

MyClass a = new A(); // internally setting "A" "B", "C" etc.
MyClass b = new B();
MyClass c = new C();
MyClass d = new D();

Когда можно написать это:

MyClass a = new MyClass("A");
MyClass b = new MyClass("B");
MyClass c = new MyClass("C");
MyClass d = new MyClass("D");

Если бы мне пришлось изменить сигнатуру метода конструктора BaseClass, мне пришлось бы изменить все подклассы.

Вот почему наследование - это артефакт, который создает ВЫСОКОЕ сцепление, что нежелательно в объектно-ориентированных системах. Его следует избегать и, возможно, заменить композицией.

Подумайте, действительно ли они вам действительно нужны как подкласс. Вот почему вы часто видите встроенные интерфейсы:

 public interface NameAware {
     public String getName();
 }



 class A implements NameAware ...
 class B implements NameAware ...
 class C ... etc. 

Здесь B и C могли унаследовать от A, что создало бы очень ВЫСОКУЮ связь между ними, при использовании интерфейсов связь уменьшается, если A решает, что он больше не будет «NameAware», другие классы не сломаются.

Конечно, если вы хотите повторно использовать поведение, это не сработает.

OscarRyz
источник
2
Да, за исключением того, что вы больше не можете гарантировать, что ваши экземпляры инициализированы должным образом (например, имеют имена в данном конкретном случае)
ChssPly76
@ ChssPly76: Да, но, вероятно, это потому, что наследование используется некачественно. Я расширил свой ответ, чтобы охватить его.
OscarRyz
4

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

В среде Eclipse IDE:

  1. откройте меню Project -> Properties или щелкните правой кнопкой мыши свой проект в Package Explorer и выберите Properties (Alt + Enter в Windows, Command + I в Mac)
  2. щелкните Путь сборки Java, затем Библиотеки вкладку
  3. выберите Modulepath или Classpath и нажмите Add Library ... кнопку
  4. выберите Системную библиотеку JRE, затем нажмите Далее
  5. оставьте выбранной JRE рабочей области по умолчанию (вы также можете выбрать другой вариант) и нажмите Готово
  6. наконец нажмите Применить и Закрыть .
йогеш кумар
источник
2

Другой способ - вызвать super () с обязательным аргументом в качестве первого оператора в конструкторе производного класса.

public class Sup {
    public Sup(String s) { ...}
}

public class Sub extends Sup {
    public Sub() { super("hello"); .. }
}
user2407102
источник
0

Eclipse выдаст эту ошибку, если у вас нет вызова конструктора суперкласса в качестве первого оператора в конструкторе подкласса.

Сагар
источник
0

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

Serj.by
источник
0

Я решил вышеуказанную проблему следующим образом:

  1. Щелкните Project.
  2. щелкните свойства> Путь сборки Java> Библиотека> Системная библиотека JRE> Изменить
  3. Выберите JRE по умолчанию и закончите
  4. Применить и закрыть.
Уттам Павар
источник
-1

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

Ура.

 abstract public class BaseClass {
        // ADD AN ARGUMENTLESS CONSTRUCTOR TO THE BASE CLASS
        public BaseClass(){
        }

        String someString;
        public BaseClass(String someString) {
            this.someString = someString;
        }
        abstract public String getName();
    }

public class ACSubClass extends BaseClass {
    public ASubClass(String someString) {
        super(someString);
    }
    public String getName() {
        return "name value for ASubClass";
    }
}
BrainO2
источник
Это упрощает создание недопустимых объектов (без someStringнабора) и, следовательно, полностью сводит на нет цель as конструктора.
Роберт
-1

У меня была эта ошибка, и я исправил ее, удалив выброшенное исключение рядом с методом в блок try / catch

Например: ОТ:

public static HashMap<String, String> getMap() throws SQLException
{

}

Кому:

public static Hashmap<String,String> getMap()
{
  try{

  }catch(SQLException)
  { 
  }
}
Томас Джонсон
источник
Этот ответ не имеет ничего общего с ошибками компилятора для отсутствующего конструктора.
Роберт