Błąd Java: Niejawny superkonstruktor jest niezdefiniowany dla domyślnego konstruktora

89

Mam prosty kod Java, który wygląda podobnie do tego w swojej strukturze:

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";
    }
}

Będę miał kilka podklas, z BaseClassktórych każda implementuje getName()metodę na swój własny sposób ( wzorzec metody szablonowej ).

Działa to dobrze, ale nie podoba mi się posiadanie redundantnego konstruktora w podklasach. To bardziej typowe i trudne do utrzymania. Gdybym miał zmienić sygnaturę metody BaseClasskonstruktora, musiałbym zmienić wszystkie podklasy.

Kiedy usuwam konstruktora z podklas, otrzymuję ten błąd w czasie kompilacji:

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

Czy to, co próbuję zrobić, jest możliwe?

Joel
źródło
1
Proszę zostawić „redundantnego” konstruktora! Utrzymuje czytelność kodu, a wszystkie nowoczesne środowiska IDE mogą go tworzyć automatycznie, więc wystarczy nacisnąć skrót.
Andreas Dolk
3
Ponowne przeczytanie własnego pytania rok później i wydaje mi się, że mogłem usunąć konstruktory (w tym w klasie bazowej), jak sugerował Matt b, a następnie użyć statycznej metody fabryki do zbudowania instancji.
Joel

Odpowiedzi:

146

Ten błąd pojawia się, ponieważ klasa, która nie ma konstruktora, ma domyślny konstruktor, który nie zawiera argumentów i jest odpowiednikiem następującego kodu:

public ACSubClass() {
    super();
}

Jednak ponieważ twoja BaseClass deklaruje konstruktor (i dlatego nie ma domyślnego konstruktora bezargumentowego, który w przeciwnym razie dostarczyłby kompilator) jest to nielegalne - klasa, która rozszerza BaseClass, nie może wywołać, super();ponieważ nie ma konstruktora bez argumentów w BaseClass.

Jest to prawdopodobnie trochę sprzeczne z intuicją, ponieważ można by pomyśleć, że podklasa automatycznie ma dowolnego konstruktora, który ma klasa bazowa.

Najprostszym sposobem obejścia tego problemu jest to, że klasa bazowa nie deklaruje konstruktora (i tym samym ma domyślny konstruktor bez argonu) lub ma zadeklarowany konstruktor bez argumentu (samodzielnie lub wraz z innymi konstruktorami). Ale często tego podejścia nie można zastosować - ponieważ potrzebujesz wszelkich argumentów, które są przekazywane do konstruktora, aby skonstruować prawidłową instancję klasy.

matowe b
źródło
17
„Jest to prawdopodobnie trochę sprzeczne z intuicją, ponieważ można by pomyśleć, że podklasa automatycznie ma dowolnego konstruktora, który ma klasa podstawowa”. +1
Mr_and_Mrs_D
2
Ze względu na potomność zasugeruję moje rozwiązanie dla przyszłych czytelników: stwórz konstruktora bez argumentów, BaseClassale spraw, aby po prostu rzucił UnsupportedOperationExceptionlub coś. To nie jest najlepsze rozwiązanie (fałszywie sugeruje, że klasa może obsługiwać konstruktor bez argonu), ale jest najlepszym, o jakim przychodzi mi do głowy.
JMTyler
49

Dla tych, którzy wygooglują ten błąd i przybywają tutaj: może być inny powód, dla którego go otrzymali. Eclipse podaje ten błąd, gdy masz konfigurację projektu - niezgodność konfiguracji systemu.

Na przykład, jeśli importujesz projekt Java 1.7 do Eclipse i nie masz poprawnie skonfigurowanego 1.7, pojawi się ten błąd. Następnie możesz przejść do Project - Preference - Java - Compileri switch to 1.6 or earlier; lub przejdź do Window - Preferences - Java - Installed JREsi dodaj / napraw instalację JRE 1.7.

MF.OX
źródło
2
Właśnie otrzymałem ten błąd bez wyraźnego powodu w Eclipse. Następnie wyczyściłem obszar roboczy (menu Projekt -> Wyczyść ...) i zniknął.
erickrf
7

Jest to możliwe, ale nie tak, jak masz.

Musisz dodać konstruktor no-args do klasy bazowej i to wszystko!

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";
    }
}

Gdy nie dodasz konstruktora (żadnego) do klasy, kompilator doda domyślny konstruktor no arg.

Gdy defualt no arg wywołuje super (); a ponieważ nie masz go w super klasie, otrzymasz ten komunikat o błędzie.

Chodzi o samo pytanie.

Teraz rozszerzając odpowiedź:

Czy zdajesz sobie sprawę, że tworzenie podklasy (zachowania) w celu określenia innej wartości (danych) nie ma sensu ?? !!! Mam nadzieję, że tak.

Jeśli jedyną zmianą jest "nazwa", to wystarczy jedna sparametryzowana klasa!

Więc nie potrzebujesz tego:

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

lub

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

Kiedy możesz to napisać:

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

Gdybym miał zmienić sygnaturę metody konstruktora BaseClass, musiałbym zmienić wszystkie podklasy.

Cóż, dlatego dziedziczenie jest artefaktem, który tworzy WYSOKIE sprzężenie, co jest niepożądane w systemach OO. Należy tego unikać i być może zastąpić kompozycją.

Pomyśl, czy naprawdę potrzebujesz ich jako podklasy. Dlatego bardzo często widzisz interfejsy używane zamiast:

 public interface NameAware {
     public String getName();
 }



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

Tutaj B i C mogliby odziedziczyć po A, co stworzyłoby wśród nich bardzo WYSOKIE sprzężenie, przy użyciu interfejsów sprzężenie jest zredukowane, jeśli A zdecyduje, że nie będzie już „NameAware”, inne klasy nie zostaną zerwane.

Oczywiście, jeśli chcesz ponownie użyć zachowania, to nie zadziała.

OscarRyz
źródło
2
Tak, z wyjątkiem tego, że nie możesz już zapewnić, że Twoje instancje są poprawnie zainicjowane (np. Mają nazwy w tym konkretnym przypadku)
ChssPly76
@ ChssPly76: Tak, ale to prawdopodobnie dlatego, że dziedziczenie jest źle używane. Rozszerzyłem swoją odpowiedź, żeby to objąć.
OscarRyz,
4

Ten błąd może się również pojawić, gdy środowisko JRE nie jest ustawione. Jeśli tak, spróbuj dodać bibliotekę systemową JRE do swojego projektu.

W Eclipse IDE:

  1. otwórz menu Projekt -> Właściwości lub kliknij prawym przyciskiem myszy swój projekt w Eksploratorze pakietów i wybierz Właściwości (Alt + Enter w Windows, Command + I na Macu)
  2. kliknij opcję Ścieżka budowania Java, a następnie kartę Biblioteki
  3. wybierz ModulePath lub Classpath i naciśnij Add Library ... przycisku
  4. wybierz JRE System Library, a następnie kliknij Next
  5. pozostaw wybrane domyślne środowisko JRE Workspace (możesz też wybrać inną opcję) i kliknij przycisk Zakończ
  6. na koniec naciśnij Zastosuj i Zamknij .
yogesh kumar
źródło
2

Innym sposobem jest wywołanie super () z wymaganym argumentem jako pierwszą instrukcją w konstruktorze klasy pochodnej.

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

public class Sub extends Sup {
    public Sub() { super("hello"); .. }
}
user2407102
źródło
0

Eclipse poda ten błąd, jeśli nie masz wywołania konstruktora superklasy jako pierwszej instrukcji w konstruktorze podklasy.

Sagar
źródło
0

Przepraszamy za nekroposty, ale właśnie dzisiaj napotkałem ten problem. Dla każdego, kto również boryka się z tym problemem - jednym z możliwych powodów - nie wywołujesz superpierwszej linii metody. Po drugie, trzecia i inne linie wywołują ten błąd. Call of super powinno być pierwszym wywołaniem w Twojej metodzie. W tym przypadku wszystko jest w porządku.

Serj.by
źródło
0

Powyższy problem rozwiązałem w następujący sposób:

  1. Kliknij Projekt.
  2. kliknij właściwości> Ścieżka budowania Java> Biblioteka> Biblioteka systemowa JRE> Edytuj
  3. Wybierz domyślny system JRE i zakończ
  4. Zastosuj i zamknij.
Uttam Pawar
źródło
-1

Możesz rozwiązać ten błąd, dodając konstruktor bez argumentów do klasy bazowej (jak pokazano poniżej).

Twoje zdrowie.

 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
źródło
Ułatwia to konstruowanie nieprawidłowych obiektów (bez someString) zestawu, a tym samym całkowicie przeczy celowi konstruktora.
Robert
-1

Miałem ten błąd i naprawiłem go, usuwając wyrzucony wyjątek spoza metody do bloku try / catch

Na przykład: OD:

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

}

DO:

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

  }catch(SQLException)
  { 
  }
}
Thomas Johnson
źródło
Ta odpowiedź nie ma nic wspólnego z błędami kompilatora dla brakującego konstruktora.
Robert