Konstruktor

Was ist ein Konstruktor

Ein Konstruktor ist eine spezielle Funktion zum Erstellen von Klasseninstanzen, die hauptsächlich zur Initialisierung von Eigenschaften dient. Außer dem Standardkonstruktor haben alle Konstruktoren denselben Namen wie die Klasse.

Einteilung der Dart-Konstruktoren:

  1. Generativer Konstruktor (Generative)
  2. Standardkonstruktor (Default)
  3. Benannter Konstruktor (Named)
  4. Konstantenkonstruktor (Const)
  5. Umleitender Konstruktor (Redirecting)
  6. Fabrikkonstruktor (Factory)
  7. Umleitender Fabrikkonstruktor
  8. Kompakte Konstruktorsyntax (Dart 3.13+)
  9. Hauptkonstruktor (Primary Constructors, minimale Felder + Konstruktor-Syntax)

1. Generativer Konstruktor Generative Constructor

Der grundlegendste Konstruktor, erstellt Instanzen und initialisiert Member-Variablen. Er unterstützt Initialisierungsparameter this.Variable zur Vereinfachung der Zuweisung.

class Point {
  double x;
  double y;

  // Generativer Konstruktor, this.x übergibt Parameter direkt an Member-Variablen
  Point(this.x, this.y); // Weist x und y direkt Werte zu
}

void main() {
  final p = Point(10, 20);
  print("(${p.x}, ${p.y})"); // (10, 20)
}
Code-Sprache: JavaScript (javascript)

2. Standardkonstruktor Default Constructor

Wenn eine Klasse manuell keinen Konstruktor definiert, stellt Dart automatisch einen parameterlosen, unbenannten generativen Konstruktor zur Verfügung. Das Verhalten entspricht anderen C-ähnlichen Sprachen wie C++, Java, C# usw.

class PointA {
  double x = 0;
  double y = 0;
  // Impliziter Standardkonstruktor: PointA();
}

void main() {
  final p = PointA(); // Ruft automatisch den Standardkonstruktor auf
  print("(${p.x}, ${p.y})"); // (0.0, 0.0)
}
Code-Sprache: PHP (php)

Hinweis: Sobald Sie einen beliebigen Konstruktor manuell schreiben, verschwindet der Standardkonstruktor.

3. Benannter Konstruktor Named Constructor

Zweck: Mehrere Konstruktoren pro Klasse mit klarer Semantik definieren; Syntaxformat Klassenname.Konstruktorname()

const double xOrigin = 0;
const double yOrigin = 0;

class Point {
  final double x;
  final double y;

  // Hauptgenerativer Konstruktor
  Point(this.x, this.y);

  // Benannter Konstruktor: Ursprungspunkt
  Point.origin() : x = xOrigin, y = yOrigin;
}

void main() {
  final p1 = Point(5, 6);
  final p2 = Point.origin(); // Aufruf des benannten Konstruktors
  print(p1.x); //5
  print("Ursprung: (${p2.x}, ${p2.y})"); // (0,0)
}
Code-Sprache: PHP (php)

Wichtige Regeln

  • Unterklassen erben keine benannten Konstruktoren von der Oberklasse; ein gleichnamiger benannter Konstruktor muss bei Bedarf manuell implementiert werden;
  • Benannte Konstruktoren unterstützen Initialisierungslisten zur Zuweisung von final-Variablen.

4. Konstantenkonstruktor Const Constructor

Dient zur Erstellung von Kompilierzeitkonstantenobjekten, Voraussetzungen:

  1. Alle Member-Variablen mit final markieren
  2. Dem Konstruktor das Schlüsselwort const voranstellen. Bei der Instanziierung mit const wird der Konstantencache wiederverwendet; ohne const wird ein reguläres neues Objekt erstellt.
class ImmutablePoint {
  static const ImmutablePoint origin = ImmutablePoint(0, 0); // Statisch zur Klasse gehörend, gemeinsam genutztes Objekt für alle Instanzen
  final double x, y;

  // Konstantenkonstruktor initialisiert final deklarierte x und y
  const ImmutablePoint(this.x, this.y);
}

void main() {
  // Kompilierzeitkonstante, nutzt dasselbe Objekt erneut
  const p1 = ImmutablePoint(1, 2);
  const p2 = ImmutablePoint(1, 2); // Nur bei identischen Parametern wird dieselbe Instanz verwendet
  print(p1 == p2); // true

  // Reguläre Instanz, neues separates Objekt
  final p3 = ImmutablePoint(1, 2);
  print(p1 == p3); // false
}Code-Sprache: PHP (php)

5. Umleitender Konstruktor Redirecting Constructor

Ein Konstruktor, der mit der Syntax : this(Parameter) die Ausführung an einen anderen Konstruktor derselben Klasse weiterleitet. Der Funktionskörper muss leer sein.

class Point {
  double x, y;

  // Hauptkonstruktor
  Point(this.x, this.y);

  // Umleitender Konstruktor: nur x übergeben, y fest auf 0 setzen, Weiterleitung an Hauptkonstruktor
  Point.alongX(double x) : this(x, 0); // Ruft benannten Konstruktor auf, übergibt x an Hauptkonstruktor und fixiert y=0
}

void main() {
  final p = Point.alongX(9);
  print("x=${p.x}, y=${p.y}"); // x=9, y=0
}
Code-Sprache: JavaScript (javascript)

6. Fabrikkonstruktor Factory Constructor

Mit dem Schlüsselwort factory markiert, Einsatzszenarien:

  1. Nicht immer neue Objekte erstellen (Cache-Wiederverwendung, Rückgabe von Unterklasseninstanzen)
  2. Komplexe Logik vor Objekterstellung (JSON-Parsing, Parameterprüfung). Einschränkung: kein Zugriff auf this
Singleton-Cache Logger Beispiel
class Logger {
  final String name;
  bool mute = false;
  // Privater Cache, nur innerhalb der Bibliothek sichtbar
  static final Map<String, Logger> _cache = {};

  // Fabrikkonstruktor: Zuerst aus Cache abrufen
  factory Logger(String name) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }

  // Fabrikkonstruktor: Erstellen aus JSON-Parsing
  factory Logger.fromJson(Map<String, dynamic> json) {
    return Logger(json["name"].toString());
  }

  // Privater interner generativer Konstruktor
  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

void main() {
  final log1 = Logger("UI");
  final log2 = Logger("UI");
  print(log1 == log2); // true, dasselbe Cache-Objekt

  final jsonLog = Logger.fromJson({"name": "Net"});
  jsonLog.log("request success");
}
Code-Sprache: JavaScript (javascript)

7. Umleitender Fabrikkonstruktor Redirecting Factory

Syntax factory Name() = KonstruktorAndereKlasse;, geeignet für abstrakte Klassen, die die Instanzerstellung delegieren und wiederholte Parameterdefinitionen vermeiden.

abstract class Listenable {}

class _MergingListenable implements Listenable {
  List<_MergingListenable> list;
  _MergingListenable(this.list);
}

// Umleitende Fabrik, ruft Konstruktor der internen privaten Klasse auf
factory Listenable.merge(List<Listenable> list) = _MergingListenable;
Code-Sprache: PHP (php)

8. Konstruktor-Tear-offs

Konstruktoren direkt als Funktionsargument übergeben, ohne () zu schreiben. Ersetzt überflüssige Lambda-Anonyme Funktionen und bietet bessere Performance.

Empfohlene Schreibweise
void main() {
  final charCodes = [65, 66, 67];
  // Benannter Konstruktor String.fromCharCode
  final strs = charCodes.map(String.fromCharCode);
  print(strs); // (A, B, C)

  // Unbenannter Konstruktor-Tear-off mit .new
  final buffers = charCodes.map(StringBuffer.new);
}
Code-Sprache: PHP (php)
Nicht empfohlen: Überflüssiges Lambda
// Überflüssige Umhüllung, vermeiden
charCodes.map((code) => String.fromCharCode(code));
Code-Sprache: JavaScript (javascript)

9. Kompakte Konstruktorsyntax (Dart 3.13+) Concise Constructor

Innerhalb der Klasse kann der Klassenname weggelassen werden, direkt new / factory zur Definition von Konstruktoren verwenden, ohne Klassenname.xxx zu schreiben:

Traditionelle SchreibweiseKurzschreibweise
Point(this.x,this.y)new(this.x,this.y)
Point.origin():x=0,y=0new origin():x=0,y=0
factory Point.clone()factory clone()
class Point {
  double x, y;

  // Kompakter unbenannter generativer Konstruktor
  new(this.x, this.y);

  // Kompakter benannter generativer Konstruktor
  new origin() : x = 0, y = 0;

  // Kompakter Fabrikkonstruktor
  factory clone(Point other) => new(other.x, other.y);
}

void main() {
  final p = new(3, 4);
  final origin = new origin();
  final copy = p.clone(p);
}
Code-Sprache: PHP (php)

10. Drei Methoden zur Initialisierung von Instanzvariablen

1: Direkte Zuweisung bei Deklaration
class PointA {
  double x = 1.0;
  double y = 2.0;
}
Code-Sprache: JavaScript (javascript)

2: Initialisierungsparameter

this.Variable

Unterstützt optionale Positionsparameter, benannte Parameter und nullable Variablen

class PointB {
  final double x;
  final double y;

  // Optionale Positionsparameter
  PointB.opt([this.x = 0, this.y = 0]);

  // Benannte Parameter mit Standardwert
  PointB.named({this.x = 5, this.y = 5});
}
Code-Sprache: JavaScript (javascript)

3: Initialisierungsliste Initializer List

Zuweisungen nach dem Doppelpunkt :, wird vor dem Konstruktorkörper ausgeführt, darf nicht auf this zugreifen, unterstützt assert zur Parameterprüfung.

class Point {
  final double x;
  final double y;

  Point.fromJson(Map<String, double> json)
      : x = json["x"]!,
        y = json["y"]! {
    print("Konstruktorkörper ausgeführt, x=$x");
  }

  // Mit Assert-Prüfung
  Point.withAssert(this.x, this.y) : assert(x >= 0);
}
Code-Sprache: JavaScript (javascript)

11. Direkte Initialisierungsparameter für private Felder (Dart3.12+)

Bei privaten Variablen mit Unterstrichprefix schreibt man Konstruktorparameter als this._Feld. Bei externem Aufruf wird der öffentliche Name ohne Unterstrich verwendet.

class Point {
  final double _x;
  final double _y;

  // Intern _x, externer Aufruf schreibt x:10
  Point({required this._x, this._y = 0});
}

void main() {
  final p = Point(x: 10); // Kein _x nötig, direkt x
  print(p._x); // 10
}
Code-Sprache: PHP (php)

In der Initialisierungsliste den ursprünglichen Namen der privaten Variable verwenden:

class Point {
  final double _x;
  Point({required this._x}) : assert(_x > 0);
}
Code-Sprache: JavaScript (javascript)

12. Oberklassenkonstruktoren und Vererbungsregeln

  1. Unterklassen erben keine Konstruktoren der Oberklasse;
  2. Ausführungsreihenfolge des Konstruktors: Initialisierungsliste → Oberklassenkonstruktor → Unterklassen-Konstruktorkörper;
  3. Wenn die Oberklasse keinen parameterlosen Standardkonstruktor besitzt, muss die Unterklasse manuell mit super() den Oberklassenkonstruktor angeben.
Einfaches super-Aufrufbeispiel

Wie Java nutzt man super zur Referenz der Basisklasse, in C# wird base verwendet.

class Person {
  final String name;
  Person(this.name);
  Person.fromMap(Map map) : name = map["name"];
}

class Employee extends Person {
  int id;
  // Aufruf des benannten Oberklassenkonstruktors
  Employee(Map data) : super.fromMap(data), id = data["id"];
}
Code-Sprache: JavaScript (javascript)

Hinweis: Innerhalb von super-Parameterausdrücken darf this nicht verwendet werden.

Super-Parameter (Dart2.17+, vereinfacht die Parameterübergabe an die Oberklasse)

Mit super.Variable Parameter direkt an den Oberklassenkonstruktor weiterreichen, ohne manuell super(x,y) schreiben zu müssen

class Vector2d {
  final double x, y;
  Vector2d(this.x, this.y);
}

class Vector3d extends Vector2d {
  final double z;
  // super.x super.y werden automatisch an den Oberklassenkonstruktor übergeben
  Vector3d(super.x, super.y, this.z);
}

// Benannter Oberklassenkonstruktor + benannte Super-Parameter
class Vector3dYZ extends Vector2d {
  final double z;
  Vector3dYZ({required super.y, required this.z}) : super.named(x: 0);
}
Code-Sprache: JavaScript (javascript)

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert