Function

In Dart sind Funktionen ebenfalls Objekte – das unterscheidet sich leicht von anderen Programmiersprachen.

Funktionen sind Objekte erster Klasse

Dart ist eine vollständig objektorientierte Sprache: Funktionen selbst sind Objekte mit dem Typ Function.

Sie können Variablen zugewiesen und als Argumente an andere Funktionen übergeben werden;

Klasseninstanzen lassen sich direkt wie Funktionen aufrufen

1 Standard-Funktionsdefinition

// Vollständige Deklaration mit Rückgabetyp- und Parametertyp-Annotationen
bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}
Code-Sprache: Dart (dart)

Vollständiges Beispiel

// Zuordnungstabelle der Ordnungszahlen von Edelgasen
final Map<int, String> _nobleGases = {
  2: 'He',
  10: 'Ne',
  18: 'Ar',
  36: 'Kr',
  54: 'Xe',
  86: 'Rn',
};

// Vollständige Deklaration mit Rückgabetyp und Parametertypen
// Der Typ int ist nicht null-fähig; nutze int?, um null-Werte zu akzeptieren
bool isNoble(int? atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

void main() {
  print(isNoble(null));       // null ist kein Schlüssel der Map → false
  print(isNoble(2));          // Passt zu einem Edelgas → true
  print(isNoble(9));          // Kein Treffer → false
  print(isNoble(86));         // Passt zu einem Edelgas → true
}
Code-Sprache: Dart (dart)
false
true
false
trueCode-Sprache: JavaScript (javascript)

2 Auslassen von Typannotationen

Der Effective Dart-Styleguide schreibt vollständige explizite Typangaben für öffentliche, nach außen sichtbare APIs vor.

Bei internen privaten Funktionen dürfen Typannotationen weggelassen werden

Beispiel


final Map<int, String> _nobleGases = {
  2: 'He',
  10: 'Ne',
  18: 'Ar',
  36: 'Kr',
  54: 'Xe',
  86: 'Rn',
};


isNoble(atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

void main() {
  print(isNoble(null));       // null ist kein Schlüssel der Map → false
  print(isNoble(2));          // Passt zu einem Edelgas → true
  print(isNoble(9));          // Kein Treffer → false
  print(isNoble(86));         // Passt zu einem Edelgas → true
}

Code-Sprache: Dart (dart)

Im obigen Beispiel verzichtet die Funktion isNoble auf den Rückgabetyp bool und den Parametertyp int?, funktioniert aber trotzdem einwandfrei.

Wenn du keine Typangaben schreibst, führt Dart eine automatische Typinferenz durch:

  • Parameter atomicNumber: Kein expliziter Typ → als dynamic abgeleitet (dynamischer Typ, akzeptiert beliebige Werte: Zahlen, Strings, null)
  • Rückgabewert: Keine vorangestellte Typannotation → Dart leitet den Rückgabetyp aus dem Ausdruck nach return als bool ab, ohne ihn im Code zu schreiben
Warum müssen öffentliche APIs immer explizite Typen haben?

Lesbarkeit: Entwickler, die deine Funktion aufrufen, sehen sofort Ein- und Ausgabetypen, ohne den internen Implementierungscode lesen und raten zu müssen;

Typsicherheit: Der Typ dynamic deaktiviert die statische Typprüfung; falsche Typen führen zu keinen Kompilierfehlern und verursachen oft Laufzeitabstürze;

IDE-Intellisense: Annotierte Typen aktivieren Autovervollständigung und Parameterhinweise im Editor; ohne Typen kann die IDE keine genauen Vorschläge anzeigen.

Wann darf man Typannotationen weglassen?

Nur empfohlen für lokalen privaten Code – Funktionen, die ausschließlich innerhalb der aktuellen Datei verwendet und nie von außen aufgerufen werden.

Pfeil-Kurzschreibweise (=>)

Wenn der Funktionskörper (Code innerhalb von {}) nur einen einzigen Rückgabeausdruck enthält, kannst du die Pfeilsyntax verwenden.

Beispiel

bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
Code-Sprache: Dart (dart)
  • => expr entspricht { return expr; };
  • Zwischen Pfeil und Semikolon dürfen nur Ausdrücke stehen; Anweisungen wie if sind verboten (nutze stattdessen ternäre Operatoren).
// Zuordnungstabelle der Ordnungszahlen von Edelgasen
final Map<int, String> _nobleGases = {
  2: 'Helium',
  10: 'Neon',
  18: 'Argon',
  36: 'Krypton',
  54: 'Xenon',
  86: 'Radon',
};

// Pfeil-Kurzfunktion: Einzeiliger Ausdruck gibt direkt zurück, äquivalent zu Blockfunktionen mit return
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

void main() {
  // Vorhandene Edelgase testen
  print(isNoble(2));   // true
  print(isNoble(18));  // true

  // Normale Elemente testen
  print(isNoble(6));   // false (Kohlenstoff)
  print(isNoble(99));  // false (kein passendes Element)
}
Code-Sprache: Dart (dart)

Parameter

Funktionen unterstützen erforderliche Positionsargumente, gefolgt von genau einer der zwei Optionen: benannte Parameter oder optionale Positionsargumente – sie lassen sich nicht mischen.

Bei der Definition oder dem Aufruf von Parametern sind nachgestellte Kommas , erlaubt, um die Code-Lesbarkeit zu verbessern.

1. Benannte Parameter Named parameters

Eingekapselt in {}, standardmäßig optional; füge required hinzu, um eine zwingende Übergabe zu erzwingen.

1) Einfache null-fähige benannte Parameter (kein Standardwert, Standardwert ist null, der Typ muss mit ? als null-fähig markiert werden)

void enableFlags({bool? bold, bool? hidden}) {}
// Bei Aufruf explizit Parameternamen angeben
enableFlags(bold: true, hidden: false);
Code-Sprache: Dart (dart)

Vollständiges Beispiel

/// Textstil-Flags aktivieren: fett, ausgeblendet
void enableFlags({bool? bold, bool? hidden}) {
  print('Fett: $bold, Ausgeblendet: $hidden');
}

void main() {
  // Alle benannten Parameter übergeben
  enableFlags(bold: true, hidden: false);

  // Nur einen benannten Parameter übergeben, der andere wird null
  enableFlags(bold: true);

  // Nur hidden übergeben
  enableFlags(hidden: true);

  // Keine Parameter übergeben, beide Werte sind null
  enableFlags();
}
Code-Sprache: Dart (dart)

Ausgabeergebnis

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Fett: true, Ausgeblendet: false
Fett: true, Ausgeblendet: null
Fett: null, Ausgeblendet: true
Fett: null, Ausgeblendet: nullCode-Sprache: JavaScript (javascript)

2) Festlegen von Kompilierzeit-Konstanten als Standardwerte (keine Nullmarkierung nötig, da ein Standardwert existiert)

void enableFlags({bool bold = false, bool hidden = false}) {}
enableFlags(bold: true); // hidden nutzt automatisch false

Code-Sprache: Dart (dart)

Beispiel

/// Textstil-Flags aktivieren: fett, ausgeblendet
void enableFlags({bool bold = false, bool hidden = false}) {
  print('Fett: $bold, Ausgeblendet: $hidden');
}

void main() {
  enableFlags();//Keine Parameter übergeben, beide nutzen Standardwert false → Fett: false, Ausgeblendet: false
}
Code-Sprache: Dart (dart)

3) Zwingende benannte Parameter mit required (Hinweis: Sehr häufig in Konstruktoren von Flutter-Widgets verwendet. Das folgende Beispiel ist Flutter-Code; mehr erfährst du in Flutter-Tutorials.)

const Scrollbar({super.key, required Widget child});
// required-Parameter akzeptieren ebenfalls null-fähige Typen
const Scrollbar({super.key, required Widget? child});

Code-Sprache: Dart (dart)

Dart schreibt nicht vor, dass benannte Parameter am Ende der Parameterliste stehen müssen; sie können beliebig eingefügt werden

repeat(times: 2, () {})Code-Sprache: CSS (css)
2. Optionale Positionsargumente Optional positional parameters

Eingekapselt in [], Standardwert ist null bei Weglassen; konstante Standardwerte können definiert werden.

// Kein Standardwert, Typ mit ? markiert
String say(String from, String msg, [String? device]) {
  var result = '$from says $msg';
  if (device != null) result = '$result with a $device';
  return result;
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
assert(say('Bob', 'Howdy', 'smoke signal') == 'Bob says Howdy with a smoke signal');

Code-Sprache: Dart (dart)

Vollständiges Beispiel

// [String? device] optionales Positionsargument ohne Standardwert, null bei Weglassen
String say(String from, String msg, [String? device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device'; //Geräteinformation an den Satz anhängen, falls device nicht null ist
  }
  return result;
}

void main() {
  // Drittes optionales Argument weglassen
  String res1 = say('Bob', 'Howdy');
  print(res1);

  // Drittes optionales Positionsargument übergeben
  String res2 = say('Bob', 'Howdy', 'smoke signal');
  print(res2);

  // Manuell null an das optionale Argument übergeben
  String res3 = say('Alice', 'Hi', null);
  print(res3);
}
Code-Sprache: Dart (dart)

Standardwerte festlegen:

String say(String from, String msg, [String device = 'carrier pigeon']) {
  return '$from says $msg with a $device';
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon');

Code-Sprache: Dart (dart)

Vollständiges Beispiel

// Optionales Positionsargument mit Standardwert carrier pigeon (Brieftaube)
String say(String from, String msg, [String device = 'carrier pigeon']) {
  return '$from says $msg with a $device';
}

void main() {
  // Drittes optionales Argument weglassen, automatisch Standardwert carrier pigeon nutzen
  String res1 = say('Bob', 'Howdy');
  print(res1); //Bob says Howdy with a carrier pigeon

  // Eigenes Gerät übergeben, um Standardwert zu überschreiben
  String res2 = say('Bob', 'Howdy', 'smoke signal');
  print(res2); //Bob says Howdy with a smoke signal

  String res3 = say('Alice', 'Hello', 'mobile phone');
  print(res3); // Alice says Hello with a mobile phone
  
  String res4 = say('Jack', 'Tom', 'website foxdevelop.com');
  print(res4); // Jack says Tom with a website foxdevelop.com
}
Code-Sprache: Dart (dart)

Ausgabeergebnis

Built firstdart:firstdart.
Bob says Howdy with a carrier pigeon
Bob says Howdy with a smoke signal
Alice says Hello with a mobile phone
Jack says Tom with a website foxdevelop.comCode-Sprache: JavaScript (javascript)


main () Einstiegsfunktion

Jedes Dart-Programm benötigt eine Top-Level-Funktion main() – sie ist der einzige Ausführungseinstiegspunkt des Programms.

  1. Basisversion ohne Parameter
void main() {
  print('Hello, welcome to foxdevelop.com!');
}

Code-Sprache: Dart (dart)
Hello, welcome to foxdevelop.com!Code-Sprache: CSS (css)
  1. Kommandozeilenprogramm mit Startargumenten
// Ausführungsbefehl: dart run firstdart.dart 1 test
void main(List<String> arguments) {
  print(arguments); // [1, test]
}

Code-Sprache: Dart (dart)

Nutze die offizielle Bibliothek args für komplexe Parsing-Aufgaben von Kommandozeilenargumenten.

Ersetze firstdart.dart oben durch deinen eigenen Dart-Dateinamen


Funktionen als Objekte erster Klasse First-class objects

1. Funktionen als Parameter übergeben

void printElement(int element) => print(element);
var list = [1, 2, 3];
list.forEach(printElement); // Funktion direkt übergeben

Code-Sprache: Dart (dart)

Vollständiges Beispiel

void main() {
    void printElement(int element) => print(element);
	var list = [1, 2, 3];
	list.forEach(printElement); // Funktion direkt übergeben
}
Code-Sprache: Dart (dart)

Ausgabe

1
2
3

2. Anonyme Funktionen einer Variable zuweisen

void main() {
    var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
	print(loudify('hello')); //!!! HELLO !!! 
}
Code-Sprache: Dart (dart)

Funktionstyp Function Type

Du kannst Typen für Funktionsvariablen explizit deklarieren

Format: Rückgabetyp Function(ParameterListe, {BenannteParameter})

void greet(String name, {String greeting = 'Hello'}) => print('$greeting $name!');
// Funktionsvariable deklarieren
void Function(String, {String greeting}) g = greet;
g('Dash', greeting: 'Howdy');

Code-Sprache: Dart (dart)

Mit typedef erstellst du Aliase für Funktionstypen, um Wiederverwendbarkeit und Lesbarkeit zu verbessern.

{String greeting}) → Ein benannter Parameter greeting vom Typ String

void main() {
    void greet(String name, {String greeting = 'Hello'}) => print('$greeting $name!');
	// Funktionsvariable deklarieren
	void Function(String, {String greeting}) g = greet;
	g('Jack', greeting: 'Howdy');
}
Code-Sprache: Dart (dart)

Ausgabe

Howdy Jack!

Anonyme Funktionen (Lambda / Closure Abschluss)

Funktionen ohne eigenen Namen, zwei Schreibweisen:

  1. Block-Syntax (mehrzeilig)
void main() {
    const list = ['apples', 'bananas'];
	var uppercaseList = list.map((item) {
	  return item.toUpperCase();
	}).toList();
	print(uppercaseList);
}
Code-Sprache: Dart (dart)

(item) { return item.toUpperCase(); } im obigen Code ist eine anonyme Funktion (Lambda)

Ausgabe: [APPLES, BANANAS]

  1. Pfeil-Kurzschreibweise (einzeiliger Ausdruck)
void main() {
    const list = ['fox', 'develop','foxdevelop.com'];
    var uppercaseList = list.map((item) => item.toUpperCase()).toList();
    uppercaseList.forEach((item) => print('$item: ${item.length}'));
}

Code-Sprache: Dart (dart)

Ausgabe

FOX: 3
DEVELOP: 7
FOXDEVELOP.COM: 14Code-Sprache: HTTP (http)

Lexikalischer Gültigkeitsbereich Lexical scope

Dart nutzt statische lexikalische Gültigkeitsbereiche: Der Gültigkeitsbereich einer Variable ergibt sich aus der Klammerhierarchie des Codes, innere Bereiche können alle Variablen äußerer Bereiche zugreifen.

bool topLevel = true;
void main() {
  var insideMain = true;
  void myFunction() {
    var insideFunction = true;
    void nestedFunction() {
      var insideNestedFunction = true;
      // Alle zugänglich
      assert(topLevel && insideMain && insideFunction && insideNestedFunction);
    }
  }
}

Code-Sprache: Dart (dart)

Lexikalische Abschlüsse Lexical closures

Abschluss: Ein Funktionsobjekt kann Variablen aus dem äußeren Gültigkeitsbereich erfassen und speichern – auch nach Verlassen des ursprünglichen Bereichs bleibt der Zugriff möglich.

Function makeAdder(int addBy) {
  // Äußere Variable addBy erfassen
  return (int i) => addBy + i;
}

void main() {
  var add2 = makeAdder(2);
  var add4 = makeAdder(4);
  assert(add2(3) == 5);
  assert(add4(3) == 7);
}

Code-Sprache: Dart (dart)

Schritt-für-Schritt-Analyse des obigen Ablaufs

Schritt 1: var add2 = makeAdder(2);

  1. makeAdder ausführen, addBy = 2 übergeben
  2. Anonyme Funktion erstellen: (int i) => 2 + i
  3. Diese Funktion der Variable add2 zuweisen. Ab dann ist add2 ein Funktionsobjekt mit dem Körper (int i) => 2 + i; es nimmt einen int-Wert i entgegen und gibt i+2 zurück
  4. Die Ausführung von makeAdder endet, aber die anonyme Funktion merkt sich dauerhaft addBy=2

Schritt 2: var add4 = makeAdder(4);

  1. makeAdder erneut ausführen, addBy = 4 übergeben
  2. Eine völlig separate neue anonyme Funktion erstellen: (int i) => 4 + i
  3. An add4 zuweisen, das eine eigene unabhängige Kopie von addBy=4 speichert und sich nicht mit add2 beeinflusst. Der Wert addBy liegt separat in den zwei Funktionsobjekten add2 und add4 gespeichert, einer hält 2, der andere 4
  • Der Aufruf von makeAdder(2) erzeugt einen unabhängigen Abschluss mit einem einzeln gebundenen Wert addBy = 2, zugewiesen an add2;
  • Der Aufruf von makeAdder(4) erzeugt einen brandneuen separaten Abschluss mit einem einzeln gebundenen Wert addBy = 4, zugewiesen an add4;
  • add2 und add4 sind vollständig getrennte Funktionsobjekte, jedes speichert privat seine erfasste addBy-Variable ohne gegenseitige Beeinträchtigung.

Schritt 3: add2(3) aufrufen

Interne Funktion ausführen: 2 + 3 = 5, Assertion erfüllt

Schritt 4: add4(3) aufrufen

Interne Funktion ausführen: 4 + 3 = 7, Assertion erfüllt

Funktions-Tear-offs

Wenn du eine Funktion / Methode ohne Klammern aufrufst, erzeugst du einen Tear-off (leichter Abschluss), der handgeschriebene anonyme Lambdas ersetzt und den Code kompakter macht.

Dies ist die empfohlene Schreibweise; übergebe einfach die Funktion direkt.

var charCodes = [68, 97, 114, 116];
var buffer = StringBuffer();
charCodes.forEach(print); // Funktions-Tear-off
charCodes.forEach(buffer.write); // Methoden-Tear-off

Code-Sprache: Dart (dart)

Langwierige äquivalente Schreibweise

charCodes.forEach((code) => print(code));
charCodes.forEach((code) => buffer.write(code));

Code-Sprache: Dart (dart)

Prüfung der Funktionsgleichheit

  • Top-Level-Funktionen, statische Methoden: == gibt true zurück, wenn die Funktionsnamen exakt übereinstimmen;
  • Instanzmethoden: Nur Tear-offs, die an die selbe Objektinstanz gebunden sind, werden als gleich gewertet.
void foo() {}
class A {
  static void bar() {}
  void baz() {}
}
void main() {
  var x = foo;
  assert(foo == x);

  x = A.bar;
  assert(A.bar == x);

  var v = A(), w = A(), y = w;
  x = w.baz;
  assert(y.baz == x); // Gleiche Instanz → gleich
  assert(v.baz != w.baz); // Verschiedene Instanzen → ungleich
}

Code-Sprache: Dart (dart)

Rückgabewerte Return values

  1. Jede Funktion hat einen Rückgabewert: Fehlt explizites return, wird am Ende implizit return null; angehängt
foo() {}
assert(foo() == null);

Code-Sprache: Dart (dart)
  1. Mehrere Werte zurückgeben: Record-Tupel verwenden
(String, int) foo() {
  return ('something', 42);
}

Code-Sprache: Dart (dart)

Getter & Setter Zugriffsmethoden

Jedes Lesen oder Zuweisen einer Eigenschaft ruft im Grunde einen Getter oder Setter auf. Normale Variablen erzeugen automatisch implizite Zugriffsmethoden; du kannst sie auch manuell explizit definieren.

Vorteil: Externer Code greift einheitlich mit .Eigenschaftsname zu, interne Logik kann jederzeit geändert werden, ohne den aufrufenden externen Code anzupassen.

String _secret = 'Hello';

// Getter für Lesezugriff
String get secret {
  print('Getter aufgerufen');
  return _secret.toUpperCase();
}

// Setter für Zuweisungen
set secret(String newMsg) {
  print('Setter aufgerufen');
  if (newMsg.isNotEmpty) _secret = newMsg;
}

void main() {
  print(secret); // Lesen, löst Getter aus → HELLO
  secret = 'Dart is fun'; // Zuweisung, löst Setter aus
  print(secret); // DART IS FUN
}

Code-Sprache: Dart (dart)

* Generatoren Generators

Erzeugen Sequenzen bedarfsgesteuert verzögert, zwei Varianten: synchron und asynchron, nutze yield zur Ausgabe von Werten.

  1. Synchroner Generator sync*, gibt Iterable zurück
Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}

Code-Sprache: Dart (dart)
  1. Asynchroner Generator async*, gibt Stream zurück
Stream<int> asyncNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

Code-Sprache: Dart (dart)
  1. Rekursiver Generator-Optimierung yield* (Delegiert an einen anderen Generator zur Leistungsverbesserung)
Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

Code-Sprache: Dart (dart)

Function

Schreibe einen Kommentar

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