Warum wird kein Standardkonstruktor generiert, wenn Sie einen expliziten Konstruktor definieren?

9
class Employee{

    String name;
    int id;

   //No explicit constructors
}

Jetzt kann ich folgende Aussage aufrufen:

Employee e1 = new Employee();

Mit dem obigen Code liefert der Compiler die Definition für den Konstruktor Employee().

Wenn ich einen einzelnen expliziten Konstruktor wie folgt definiere:

 class Employee{

    String name;
    int id;

    Employee(String aName){
        this.name=aName;
    }
}

Warum kann ich die folgende Aussage nicht aufrufen:

Employee e2 = new Employee();

Obwohl der Compiler weiß, wie man eine Definition von bereitstellt Employee().

Employee(String aName)Warum kann ich keinen Standardkonstruktor verwenden , nur weil ich einen expliziten Konstruktor definiert habe ?

Wenn der Compiler die Verwendung des Standardkonstruktors auch nach der Definition eines expliziten Konstruktors zugelassen hätte, hätte dies uns geholfen, den Code für den Standardkonstruktor wiederzuverwenden.

Harish_N
quelle
7
Es wäre kein Standardkonstruktor mehr. Ich wäre ein ständiger Konstruktor, weil es immer da ist.
Reactgular

Antworten:

6

Zunächst wird kein Standardkonstruktor generiert, der vom Compiler bereitgestellt wird, wenn der Konstruktor ohne Argumente nicht explizit geschrieben wird.

Wenn Sie keinen Konstruktor ohne Argumente explizit für eine Klasse schreiben, beschwert sich der Compiler nicht, solange Objekte ohne Parameterkonstruktoren erstellt werden (da der Compiler dem Standardkonstruktor das Erstellen von Objekten ermöglicht, wodurch selbst der Konstruktor ohne Argumente aufgerufen wird ).

Wenn Sie jedoch einen Konstruktor ohne Argumente definieren, prüft der Compiler beim Kompilieren den Aufruf des Konstruktors und seine Definition in der Klasse. Es ist so, als würde man eine andere Methode einer Klasse authentifizieren.

Es wird also ein Fehler ausgegeben, wenn Sie nach dem Definieren eines parametrisierten Konstruktors einen Konstruktor ohne Argumente aufrufen, da kein Konstruktor ohne Argumente explizit in der Klasse definiert ist. Dies ist logisch korrekt, da dies eine gute Möglichkeit ist, wenn Sie die Erstellung von Objekten ohne Daten blockieren möchten.

Angenommen, einem Mitarbeiterobjekt muss eine Mitarbeiter-ID zugeordnet sein. Definieren Sie dazu einen einzelnen Argumentkonstruktor und keinen Konstruktor ohne Argumente.

Tyson
quelle
39

Ein Konstruktor mit Argumenten ist nicht nur eine praktische Abkürzung für die Verwendung von Setzern. Sie schreiben einen Konstruktor, um sicherzustellen, dass ein Objekt niemals existiert, ohne dass bestimmte Daten vorhanden sind.

Wenn es keine solche Anforderung gibt, gut. Wenn es jedoch einen gibt, wie aus der Tatsache hervorgeht, dass Sie einen solchen Konstruktor geschrieben haben, ist es unverantwortlich, einen Standardkonstruktor zu generieren, mit dem ein Client die Regel "Kein Objekt ohne Daten" umgehen kann. Doppelt so, weil der automatisch generierte Standardkonstruktor für einen zufälligen Codeleser unsichtbar ist , was die Tatsache verbirgt, dass er existiert! Nein, wenn Sie Konstruktoren mit Argumenten und einem Standardkonstruktor möchten , müssen Sie den Standardkonstruktor selbst schreiben. Es ist sowieso nicht so, als wäre es ein großer Aufwand, einen leeren Block zu schreiben.

Kilian Foth
quelle
4
Gut gesagt, wenn ich einen Konstruktor mit Parametern schreibe, liegt das daran, dass diese Werte benötigt werden, damit die Klasseninstanzen funktionieren. Wenn ich einige nützliche Standardeinstellungen habe, schreibe ich einen Konstruktor ohne Argumente, der den anderen Konstruktor aufruft, während ich diese Standardeinstellungen bereitstelle.
Jwenting
+1. Wenn Sie nicht garantieren können, dass Felder im Konstruktor initialisiert werden, können Sie niemals unveränderliche Objekte haben, was Sie bevorzugen sollten.
Doval
2
@Doval: was Sie in bestimmten Situationen bevorzugen sollten. Wie alles andere haben unveränderliche Objekte Kompromisse, die sie für bestimmte Situationen und nicht für andere gut machen. Keine Silberkugel.
Whatsisname
1
@whatsisname Nannte es nie eine Silberkugel, aber es ist die bessere Standardeinstellung.
Doval
Diese Antwort ist sehr hilfreich in Verbindung mit der ebenfalls guten Antwort von Konrad Morawski zu lesen: Seine Antwort bietet eine schöne reale Analogie, um im Wesentlichen dieselbe Antwort zu erklären.
Cellepo
8

Java, das einen parameterlosen Konstruktor generiert, wenn Sie keinen anderen haben, ist wie ein höflicher Kellner, der Ihren Mantel für Sie nimmt.

Java, das nach dem Definieren einer anderen Version immer noch einen parameterlosen Konstruktor generiert, ist wie derselbe Kellner, der Ihnen den Mantel auszieht, nachdem Sie klar angegeben haben, dass Sie Ihre eigenen Pläne haben, was Sie mit dem Mantel tun sollen.

Wenn ich eine Klasse habe (die ich unveränderlich sein möchte):

class Person
{
    final String firstName;
    final String lastName;

Und ich füge einen Konstruktor hinzu:

    Person(String firstName, String lastName) 
    {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

Und Java sollte immer noch einen standardmäßigen, parameterlosen Konstruktor bereitstellen, dann kann dieser Code nicht kompiliert werden, da firstNameund lastNameFelder als endgültig deklariert werden, aber nach dem Aufruf nicht festgelegt werden Person p = new Person().

Sie zwingen mich, eine weitere Implementierung bereitzustellen:

private Person() 
{
    this.firstName = null; // or "", or whatever
    this.lastName = null;
}

Und da ich es nicht will - da es nutzlos ist, könnte ich geneigt sein, Totenkopf darauf zu legen:

@Deprecated
private Person() 
{
    // don't use this constructor! i don't want it to ever be called!
    throw new RuntimeException("Illegal constructor called");
}

Aber ich kann einem anderen Entwickler immer noch nicht verbieten, eine Methode zu erstellen (innerhalb der PersonKlasse hat es also nicht geholfen, den Konstruktor als privat zu markieren):

public static Person createPerson()
{
    return new Person(); // this will blow in our face
}

All diese Aufregung könnte - und wird - vermieden werden, da Java (und nicht nur Java) so funktioniert, wie es funktioniert.

Wenn Sie eine Methode definieren setCoordinates(int x, int y), erwarten Sie nicht, dass der Compiler automatisch eine parameterlose Version davon akzeptiert setCoordinates(). Es würde nichts tun.

Wie unterscheidet sich dies von der Erwartung eines parameterlosen Konstruktors? Nun, offensichtlich macht ein Konstruktor immer mindestens eine Sache - er erstellt ein Objekt (oder stirbt beim Versuch).

Aber ich möchte die Kontrolle darüber haben, wie meine Objekte instanziiert werden sollen. Wenn ich gezwungen werde, einen parameterlosen Konstruktor zu haben, egal was ich tue, wird mir diese Kontrolle genommen.

Konrad Morawski
quelle
Diese Antwort ist sehr hilfreich in Verbindung mit der ebenfalls guten Antwort von Kilian Foth zu lesen. Ich liebe die Kreativität der Kellner / Mantel Erklärung / Analogie, die gültig ist und funktioniert!
Cellepo
3

Die Sprache gibt Ihnen den Standardkonstruktor als Gefallen. Die Vermutung ist, dass, wenn Sie einen benutzerdefinierten Konstruktor geschrieben haben, Ihr Konstruktor ohne Argumente möglicherweise auch besondere Aufmerksamkeit erfordert. Kein guter Grund, aber auch nicht schrecklich.

Der sekundäre Grund ist, dass Cruft sich nur auf diese Sprache stapelt, ohne an ihre Symmetrie mit der bestehenden Praxis zu denken und sehr wenig Interesse daran zu haben, sie lesbar zu machen. Stoustroups ursprüngliches "C with Classes" war ein gehackter C-Präprozessor, der es wahrscheinlich nicht aus dem Labor schaffen sollte. Die Welt ist so lustig.

msw
quelle
3

Wenn Sie einen Konstruktor mit einem Parameter definieren, muss dies einen gültigen Grund haben. Möglicherweise ist dieser Parameter für Ihre Klasse so wichtig, dass der Objektstatus ohne ihn ungültig ist. Aus diesem Grund erstellt Java keinen Konstruktor ohne Argumente für Sie, wenn Sie bereits einen definieren.

Wenn Sie der Meinung sind, dass es praktisch wäre, einen Konstruktor ohne Argumente zu haben, können Sie ihn trotzdem definieren:

Employee(){
  // good practice to call existing constructor with your favorite value
  this("");
}
MaxZoom
quelle