- Häufige Fragen im Fachgespräch – Objektorientierung – Anwendungsentwickler-Podcast #2
- Häufige Fragen im Fachgespräch – Kapselung – Anwendungsentwickler-Podcast #6
- Häufige Fragen im Fachgespräch – Vererbung – Anwendungsentwickler-Podcast #8
- Häufige Fragen im Fachgespräch – Polymorphie – Anwendungsentwickler-Podcast #10
- Häufige Fragen im Fachgespräch – Programmierung (Teil 1) – Anwendungsentwickler-Podcast #12
- Häufige Fragen im Fachgespräch – Programmierung (Teil 2) – Anwendungsentwickler-Podcast #13
- Relationale Datenbanken – Häufige Fragen im Fachgespräch – Anwendungsentwickler-Podcast #17
- Normalisierung – Häufige Fragen im Fachgespräch – Anwendungsentwickler-Podcast #18
- SQL – Häufige Fragen im Fachgespräch – Anwendungsentwickler-Podcast #19
- Erläuterung der Gehaltsabrechnung – Häufige Fragen im Fachgespräch – Anwendungsentwickler-Podcast #46
- Zusammensetzung des Stundensatzes – Häufige Fragen im Fachgespräch – Anwendungsentwickler-Podcast #47
- Testverfahren für Software – Häufige Fragen im Fachgespräch – Anwendungsentwickler-Podcast #94
- Unit-Tests – Häufige Fragen im Fachgespräch – Anwendungsentwickler-Podcast #95
- Rückblick auf die Fachgespräche der Sommerprüfung 2022 (Fachinformatiker:in Anwendungsentwicklung) – IT-Berufe-Podcast #177
- Handelskalkulation, Skonto und Rabatt – Häufige Fragen im Fachgespräch – Anwendungsentwickler-Podcast #148
In der dreizehnten Episode meines Anwendungsentwickler-Podcasts beende ich meine Liste der häufigen Fragen rund um die Programmierung im Fachgespräch.
TL;DR
Für dein Fachgespräch als angehende:r Fachinformatiker:in Anwendungsentwicklung habe ich weitere Fragen rund ums Programmieren zusammengestellt. Zu den Grundlagen gehören this und static, gültige Objektzustände durch Konstruktoren sowie Enums und Listen. Bei Exceptions solltest du erklären können, wie Fehler weitergereicht und mit try und catch behandelt werden. Die vertiefenden Fragen reichen vom Refactoring mit automatischen Tests über Typinferenz und generische Klassen bis zur Ko- und Kontravarianz.
Kapitelmarken
- [00:01] Informationen zum Fachgespräch
- [00:37] Weitere Programmierfragen für das Fachgespräch
- [01:26] this und die aktuelle Objektinstanz
- [02:51] static und der Zugriff über die Klasse
- [04:48] Statische Methoden testen und polymorphes Überschreiben
- [06:27] Konstruktoren und sprachabhängige Syntax
- [08:22] Konstruktorparameter und gültige Objektzustände
- [10:01] Enumerations und erlaubte Werte
- [10:57] Listen und Arrays
- [13:04] Exceptions und Fehlerinformationen
- [14:45] try, catch und throw
- [15:53] Checked und Unchecked Exceptions in Java
- [17:24] Vertiefende Fragen im Fachgespräch
- [17:57] Refactoring und gleichbleibendes Verhalten
- [20:01] Refactoring mit automatischen Tests absichern
- [20:49] Typinferenz am Beispiel von C#
- [23:14] Generische Klassen und Typsicherheit
- [26:04] Kovarianz und Kontravarianz bei generischen Typen
- [28:37] Varianz anhand von Methodenparametern
- [30:08] Shownotes und Feedback
- [31:08] Newsletter und Listen für die Prüfung
Objektorientierung
- Was bedeutet das Schlüsselwort
this?- Zeiger auf das aktuelle Objekt.
- Was bedeutet
static?- Statische Attribute oder Methoden können ohne eine Instanz ihrer Klasse verwendet werden.
- Was sind Vor-/Nachteile
static?- Statische Methoden sind gut testbar, da sie keinen Objektzustand verwenden. Sie können aber z.B. nicht überschrieben werden.
- Was ist ein Konstruktor?
- Ein Konstruktor ist eine besondere Methode, die eine Objektinstanz erzeugt.
- Welche Parameter sollte ein Konstruktor haben?
- Ein Konstruktor sollte alle Parameter bekommen, die nötig sind, um ein Objekt in einem gültigen Zustand zu erzeugen.
- Was sind Enumerations?
- Enumerations sind Datentypen mit einer endlichen Menge an erlaubten Werten.
- Welche Vorteile bieten Listen gegenüber Arrays?
- Sie sind redimensionierbar. Sie sind evtl. schneller.
Fehlerbehandlung
- Was sind Exceptions?
- Eine Exception beschreibt eine Fehlersituation in einem Programm. Sie kann in anderen Teilen des Programms behandelt werden.
- Welche zwei Typen von Exceptions gibt es in Java?
- Checked (müssen gefangen werden) und unchecked Exceptions.
- Was bedeuten die Schlüsselwörter
try,catch,throwundthrows?try/catch: umschließen potentiell fehlerhaften Code,throwwirft eine Exception,throwskennzeichnet eine Methode als potentiell fehlerhaft.
Tiefergehendes
- Was ist Refactoring?
- Die semantikinvariante Modifikation von Quelltext.
- Was ist Typinferenz?
- Durch Typinferenz kann der Compiler den Datentyp einer Variablen ableiten, ohne dass er explizit angegeben werden muss.
- Was sind generische Klassen?
- Generische Klassen (Generic) stellen allgemeingültige Algorithmen für unterschiedliche Datentypen bereit.
- Was sind Ko- und Kontravarianz?
- Bei einer Vererbungsbeziehung kann eine Methode in der Subklasse überschrieben werden und dabei ihre Parametertypen anhand deren Vererbungsbeziehungen verändern. Wenn diese Veränderung in die gleiche Richtung geht wie die Vererbungsbeziehung der Methode, spricht man von Kovarianz. Wenn sie gegenläufig ist, spricht man von Kontravarianz.
- Beliebtes Beispiel in Java: Listen sind nicht kovariant, d.h.
List<String>erbt nicht vonList<Object>.
Literaturempfehlungen
*
(direkt beim Rheinwerk-Verlag bestellen*)- Das passende Kapitel im OpenBook: 9 Grundlagen der Programmierung – Rheinwerk Computing :: IT-Handbuch für Fachinformatiker
*
(direkt beim Rheinwerk-Verlag bestellen*)
Links
- Permalink zu dieser Podcast-Episode
- RSS-Feed des Podcasts
- Aufzählungstyp – Wikipedia
- Ausnahmebehandlung – Wikipedia
- Refactoring – Wikipedia
- Typinferenz – Wikipedia
- Generischer Typ – Wikipedia
- Kovarianz und Kontravarianz – Wikipedia
Weitere Infos zum Fachgespräch
Du suchst noch mehr Tipps rund um das Fachgespräch? Dann schau doch mal in diese Artikel- und Podcast-Kategorie: Alle Artikel rund um das Fachgespräch.
Kennst du schon meine Hörbücher zur Vorbereitung auf das Fachgespräch? Unter dasperfektefachgespraech.de kannst du sie herunterladen. In insg. über 10 Stunden gehe ich über 200 Fragen im Detail durch und gebe Tipps für die Beantwortung.
Und wenn du dich für meinen Newsletter einträgst, bekommst du immer direkt alle Neuigkeiten von dieser Seite in dein Postfach geliefert.
Jetzt anmelden!
Zusammenfassung
Objektorientierung und Datentypen
this: auf das aktuelle Objekt zugreifen
Mit this greifst du innerhalb einer Klasse auf die aktuelle Instanz zu. Wenn ich beispielsweise eine Klasse Auto programmiere, steht der spätere Variablenname eines erzeugten Objekts noch offen. Über this kann ich bereits im Klassencode auf das jeweilige Objekt zugreifen.
static: Attribute und Methoden an der Klasse
- Statische Attribute und Methoden gehören zur Klasse. Du kannst sie über die Klasse ansprechen, ohne vorher ein Objekt zu erzeugen.
- Als Beispiel dient eine hypothetische allgemeine Höchstgeschwindigkeit, die an der Klasse
Autohinterlegt wird. - Javas
public static void mainlässt sich beim Programmstart aufrufen, bevor die ersten eigenen Objekte entstehen. - Beim Testen einer statischen Methode entfällt das Vorbereiten einer Instanz samt ihrem Zustand. Das vereinfacht den Testaufbau.
- Statische Methoden sind an die konkrete Klasse gebunden. Polymorphes Überschreiben ist bei ihnen ausgeschlossen.
Konstruktoren: Objekte in einem gültigen Zustand erzeugen
Wenn du ein Objekt erzeugst, wird der Konstruktor als besondere Methode aufgerufen. In Java trägt er exakt den Namen der Klasse und hat keinen Rückgabetyp. Die Syntax hängt von der Sprache ab: In Java und C# steht beispielsweise new vor dem Klassennamen, während der Aufruf in Ruby als Klassenname.new erfolgt.
Für meine Azubis gehört zum Konstruktor vor allem, dass das erzeugte Objekt anschließend gültig ist:
- Über die Parameter erhält er alle Werte, die dafür erforderlich sind.
- Er prüft fachliche Grenzen, etwa einen Kilometerstand von mindestens null.
- Optionale Attribute können gültige Standardwerte erhalten. Überladene Konstruktoren erlauben unterschiedliche Parameterlisten und setzen fehlende Angaben auf passende Standardwerte.
Damit werden fehlerhafte Objektzustände bereits beim Erzeugen abgefangen, bevor das Objekt durch weitere Teile des Programms gereicht wird.
Enumerations: erlaubte Werte festlegen
Mit einer Enumeration, kurz Enum, definierst du einen Datentyp mit einer endlichen Menge erlaubter Werte. Ein Beispiel sind die sieben Wochentage. Die benannten Werte machen den Code verständlich und begrenzen die möglichen Angaben auf die festgelegte Auswahl.
Listen und Arrays
- Ein Array hat eine feste Länge, die beim Anlegen feststeht. Seine Elemente liegen als zusammenhängender Block im Speicher.
- Eine Liste lässt sich in ihrer Größe verändern. Du kannst Elemente hinzufügen oder entfernen.
- Bei einer verketteten Liste verweisen die Elemente auf ihre Nachfolger beziehungsweise Vorgänger. Dadurch können sie an unterschiedlichen Stellen im Speicher liegen.
- Eine ArrayList baut auf einem Array auf und stellt Listenfunktionen bereit. Wie schnell eine Liste arbeitet, hängt von der konkreten Datenstruktur und der Programmiersprache ab.
Fehler mit Exceptions behandeln
Was eine Exception enthält
Eine Exception beschreibt eine Fehlersituation und wird als Objekt einer entsprechenden Klasse dargestellt. Sie enthält Informationen zum Fehler sowie einen Stacktrace, über den sich die beteiligten Aufrufe und die Fehlerstelle nachvollziehen lassen.
Über die Aufrufkette kann eine Exception bis zu einer Stelle weitergereicht werden, an der der Fehler behandelt wird. In Java gehören Exception und Error zur Hierarchie unter Throwable.
Die Schlüsselwörter
tryumschließt Code, bei dem ein Fehler auftreten kann, beispielsweise beim Verbinden mit einer Datenbank.catchfängt eine passende Exception ab. Dort reagierst du etwa auf eine unerreichbare Datenbank oder falsche Zugangsdaten.- Mit
throwlöst du selbst eine Exception aus. throwskennzeichnet in Java an einer Methode, welche Exceptions sie weitergeben kann.
Die Begriffe hängen von der Sprache ab. Ruby verwendet beispielsweise raise, um eine Exception auszulösen.
Checked und Unchecked Exceptions in Java
Bei Checked Exceptions muss der aufrufende Code den Fehler behandeln oder über eine eigene throws-Angabe weiterreichen. Diese Pflicht wird beim Kompilieren geprüft.
Für Unchecked Exceptions ist das Abfangen freiwillig. Sie können zur Laufzeit auftreten; die Sprache verlangt dafür keine ausdrückliche Behandlung bei jedem Methodenaufruf.
Vertiefende Fragen im Fachgespräch
Die folgenden Themen gehören zu den Fragen, die ich eher einem/einer besonders gut vorbereiteten IT-Azubi stelle. Wie tief ich nachfrage, hängt auch von der eingesetzten Sprache ab.
Refactoring und automatische Tests
Beim Refactoring veränderst du die interne Struktur des Quelltexts, während das nach außen sichtbare Verhalten gleich bleibt. Dazu kannst du Variablen verständlicher benennen oder Teile des Codes in eigene Methoden auslagern. Entwickler:innen können den Code anschließend leichter lesen.
Zur Absicherung führe ich automatische Tests, insbesondere Unit-Tests, vor und nach dem Refactoring aus. Sie prüfen, ob der Code weiterhin das erwartete Verhalten zeigt, und helfen dabei, versehentlich eingebaute Fehler zu erkennen.
Typinferenz
Durch Typinferenz leitet der Compiler den Datentyp einer Variablen aus dem zugewiesenen Wert ab. In C# ergibt beispielsweise var test = "Hallo Welt"; eine Variable vom Typ string.
Der abgeleitete Typ bleibt fest. Weitere Werte, die du dieser Variablen zuweist, müssen zu diesem Typ passen. Typinferenz gehört hier zur statischen Typisierung und spart dir das ausdrückliche Hinschreiben des Datentyps.
Generische Klassen
Mit Generics lässt sich eine allgemeine Implementierung für unterschiedliche Datentypen bereitstellen. Eine generische Liste enthält beispielsweise dieselbe Logik zum Verwalten ihrer Elemente, unabhängig vom konkreten Elementtyp.
Beim Anlegen legst du den Typ fest, etwa mit List<string>. Dadurch prüft der Compiler, ob eingefügte Werte passen, und beim Auslesen steht der Elementtyp bereits fest. Wiederholte Casts entfallen.
In einer dynamisch typisierten Sprache wie Ruby wird zur Laufzeit geprüft, ob die Elemente die erforderlichen Methoden bereitstellen.
Kovarianz, Kontravarianz und Invarianz
Bei generischen Typen lässt sich fragen, wie sich die Beziehung ihrer Typparameter auf die Beziehung der gesamten Typen auswirkt:
- Kovarianz bedeutet, dass die Typbeziehung in derselben Richtung verläuft wie bei den enthaltenen Typen.
- Kontravarianz beschreibt eine Typbeziehung in umgekehrter Richtung.
- Invarianz bedeutet, dass der betreffende Typ unverändert bleiben muss.
In Java sind List<String> und List<Object> invariant. Obwohl String von Object erbt, lassen sich diese beiden Listentypen deshalb nicht gegenseitig ersetzen.
Die Begriffe lassen sich auch anhand hypothetischer Methodenparameter beschreiben: Wird ein Parameter Auto in einer abgeleiteten Klasse zu BMW verengt, entspricht die Richtung einer Kovarianz. Eine Erweiterung von BMW zu Auto beschreibt die kontravariante Richtung. Bei einem invarianten Parameter bleibt Auto erhalten.
Weitere Materialien von mir
- Unter dasperfektefachgespräch.de findest du weitere Informationen zum Fachgespräch.
- Über meinen Newsletter erhältst du eine Liste mit über 100 häufigen Problemen in der Projektdokumentation sowie wöchentlich Hinweise auf neue Inhalte. Weitere Listen zum Projektantrag und zur Projektpräsentation sind angekündigt.
Transkription der gesamten Episode
Automatisch erzeugte Transkription der Episode
Hi, hier ist Stefan. In dieser Podcast-Episode geht es ums Fachgespräch.
Wenn du noch mehr Informationen zum Fachgespräch von mir haben willst,
dann schau doch mal auf meiner Website vorbei, dasperfektefachgespräch.de.
Und jetzt wünsche ich dir viel Spaß mit der Podcast-Episode.
Herzlich willkommen zum Anwendungsentwickler-Podcast,
dem Podcast rund um die Ausbildung zum Fachinformatiker für Anwendungsentwicklung.
In dieser Episode schließe ich meine häufigen Fragen im Fachgespräch rund um die Programmierung ab.
Viel Spaß dabei!
Hallo, zurück zu den häufigen Fragen im Fachgespräch.
Ich hatte es in der letzten Episode ja nicht ganz geschafft, die Fragen zu Ende durchzugehen,
denn ich habe noch einige auf der Liste stehen.
Deswegen habe ich bei den Eigenschaften von Programmiersprachen aufgehört.
Heute nehmen wir uns dann die letzten drei Punkte vor,
die Objektorientierung, die Fehlerbehandlung und dann noch ein paar tiefer gehende Fragen.
Schauen wir mal, wie lange das heute dauert.
Ich hoffe, ich komme mit einer halben Stunde hin.
Ich lege einfach mal los und setze nahtlos da an, wo wir beim letzten Mal aufgehört haben.
Die Objektorientierung, da haben wir noch so ein paar Sachen vergessen.
Vergessen nicht, aber die habe ich nicht so sinnvoll unterbringen können
in den bisher behandelten Fragen rund um die Objektorientierung.
Deswegen habe ich noch mal so ein paar Sachen zusammengeschrieben,
die man im Rahmen der Objektorientierung noch fragen kann.
Und das geht gleich los mit dem Schlüsselwort This.
Was macht das This?
This, das sollte man nicht meinen, das ist etwas,
was man als Objektorientierter-Entwickler eigentlich fast täglich benutzt,
aber viele können es trotzdem nicht erklären, was dahintersteckt.
Und This ist eigentlich relativ einfach.
Das ist ein Zeiger auf das aktuelle Objekt.
Meinen Programmierlehrlingen bringe ich das immer wie folgt bei.
Wenn ich ein Objekt instanziiere, dann gebe ich der Variablen,
der ich dieses Objekt zuweise, ja einen Namen.
Als Beispiel Auto, BMW gleich New Auto und dann gebe ich da die Parameter rein.
Und über diesen Namen, BMW in diesem Fall, kann ich eben auf dieses Objekt zugreifen,
kann dort Methoden aufrufen und Eigenschaften auslesen und so weiter.
Aber was ist denn, wenn ich diese Klasse Auto jetzt programmiere
und ich noch gar nicht weiß, wie die Variable später heißt,
der ich zugewiesen werde als Klasse Auto?
Das kann ich ja nicht wissen, das liegt ja beim Aufrufer.
Da brauche ich aber jetzt ja irgendeine Möglichkeit, um auf mich selbst quasi zuzugreifen,
ohne einen expliziten Namen dafür zu vergeben.
Und für diesen Zeiger auf mich selbst, da muss ich irgendeinen Namen definieren
und dieses ist eben This.
Das heißt, das This zeigt einfach auf mich selbst,
auf die aktuelle Instanz der Klasse, in der ich gerade programmiere.
Das war es schon.
Dann kommen wir zu einem weiteren Punkt, nämlich Static.
Static ist noch ein Schlüsselwort, das ich mal am Rande erläutert habe,
aber was man für sich alleine stehend auch nochmal gut nachfragen kann.
Was bedeutet denn jetzt Static?
Static, oder wenn ich es übersetzen würde, eben der statische Kontext,
das kann ich vor Attribute oder Methoden oder auch Klassen schreiben,
aber wir nehmen jetzt mal Attribute und Methoden,
die können benutzt werden, ohne eine Instanz ihrer Klasse zu haben.
Das heißt, wenn ich bei dem Beispiel Auto von eben bleibe,
dann kann ich schlecht bei einem Auto Gas geben,
wenn ich kein Objekt dieser Klasse Auto dann habe.
Ich kann nicht einfach sagen, Auto, gib Gas,
sondern ich brauche halt eben ein konkretes Auto, um da Gas zu geben.
Es gibt aber auch Methoden oder Eigenschaften,
die völlig unabhängig von einer Instanz sind, sondern die allgemeingültig sind.
Wie zum Beispiel, angenommen wir würden das da ablegen wollen,
die zulässige Höchstgeschwindigkeit auf deutschen Autobahnen.
Angenommen die gäbe es und die läge bei 130 kmh,
dann wäre es ja blöd, wenn ich die an einem Objekt hinterlege
und jedes Mal ein konkretes Auto fragen muss, wie die Höchstgeschwindigkeit ist.
Das wäre unsinnig.
Stattdessen würde ich das an die Klasse Auto hängen
und dann diesen Zugriff über die Klasse regeln.
Damit das funktioniert, müssen eben diese Methoden oder Attribute static sein.
Static oder der statische Kontext, das ist auch das, was ausgeführt wird,
bevor das Programm wirklich gestartet wird, wenn man so will.
Wenn ich mal an Java denke, da habe ich meine public static void main.
Diese main Methode kann nur deswegen aufgerufen werden, weil die static ist.
Denn wenn sie nicht static wäre, müsste ich sie an einem Objekt aufrufen,
aber zum Zeitpunkt des Programmstarts gibt es ja noch gar keine Objekte,
denn das Programm läuft ja noch gar nicht.
Also an welchem Objekt sollte ich diese Methode aufrufen?
Da gibt es keins, das geht nicht.
Deswegen muss die static sein.
Also alles, was im statischen Kontext läuft, das führt die Laufzeit der Umgebung aus,
bevor meine ersten Objekte instanziiert und ausgeführt werden.
Deswegen sind die static.
Jetzt haben diese static Methoden insbesondere leider einige Nachteile,
aber auch einige Vorteile.
Der große Vorteil ist, da sie nicht an einem Objekt hängen,
sind sie auch nicht abhängig vom Zustand eines Objekts.
Wie zum Beispiel beim Auto, wenn ich da Gas gebe,
dann hängt natürlich die Änderung des Objekts davon ab,
welche Geschwindigkeit das Auto denn aktuell hat.
Wenn es schon mit Höchstgeschwindigkeit fährt,
dann kann ich so viel Gas geben, wie ich will, es wird nicht mehr schneller.
Wenn ich das jetzt zum Beispiel testen will, brauche ich aber auch ein Auto,
was zum Beispiel im Stillstand ist, ein Auto, was normale Geschwindigkeit fährt,
ein Auto, was Höchstgeschwindigkeit fährt und so weiter.
Und jeder dieser Zustände sorgt dafür, dass sich die Methode unterschiedlich verhält,
was wir auch wollen, was ja auch sinnvoll ist.
Aber das macht das Testen etwas schwieriger.
Wenn ich dagegen eine statische Methode habe,
dann ist die von keinem Zustand irgendeines Objekts abhängig.
Das heißt, ich kann die einfach aufrufen und kriege als Ergebnis etwas zurück.
Das heißt, ich bin in meinem Setup für den Test deutlich einfacher.
Ich brauche nämlich kein Setup.
Ich kann die Methode einfach aufrufen und kriege etwas zurück und teste dagegen.
Fertig.
Bei einer Instanzmethode müsste ich erst ein Objekt erzeugen,
das Objekt in einen gültigen Zustand versetzen,
in einen bestimmten Zustand für den Test, den ich durchführen will,
und dann kann ich die Methode erst aufrufen.
Also, sehr umfangreich zu testen.
Und das funktioniert bei statischen Methoden deutlich einfacher.
Auf der anderen Seite haben sie auch einen Nachteil.
Zum Beispiel kann ich sie nicht überschreiben.
Wenn ich statische Methoden habe, dann sind die an der Klasse definiert
und die sind nicht polymorph.
Das heißt, wenn ich jetzt eine Vererbung habe,
eine Klasse, die von meinem Auto zum Beispiel erbt,
dann kann die diese statische Methode nicht überschreiben.
Sondern es wird immer die Methode an der konkreten Klasse aufgerufen
und ich kann sie nicht polymorphen mit einer anderen Implementierung versehen.
Das geht dann leider nicht.
Ja, dann kommen wir zu einer ganz anderen Frage,
aber ungefähr auch in dem Bereich Methoden wenigstens.
Nämlich, was ist ein Konstruktor?
Ein Konstruktor ist eine ganz besondere Methode,
die eine Objektinstanz erzeugt.
Das heißt, die ist dafür zuständig, aus einer Klasse ein Objekt zu instanziieren.
Und diese Methode, die ich da aufrufe,
die hat zumindest in Java und auch in vielen anderen Polymer-Sprachen
ein ganz bestimmtes Aussehen, eine ganz bestimmte Signatur.
In Java ist das zum Beispiel so,
dass der Konstruktor den Namen der Klasse 1 zu 1 übernehmen muss,
also inklusive camelCase und so weiter,
und keinen Rückgabewert hat.
Das heißt, wenn ich mir eine übliche Methodendefinition angucke,
dann habe ich eben Rückgabewert, Name, Parameter.
Ein Konstruktor hat keinen Rückgabewert,
hat denselben Namen wie die Klasse
und hat dann ganz normal die Parameter.
Daran erkenne ich quasi in Java einen Konstruktor.
Das muss aber nicht so sein.
In anderen Polymer-Sprachen ist das auch anders.
Zum Beispiel in PHP,
da hat der Konstruktor den definierten Namen
unterstrich, unterstrich, initialize.
Und damit wird dann zum Beispiel ein Objekt instanziiert.
In Ruby heißt der Konstruktor einfach new,
wie eine ganz normale Methode.
Und wenn ich den Konstruktor in Polymer-Sprachen aufrufen will,
dort ist es eigentlich fast überall identisch.
Da mache ich es nämlich mit dem Schlüsselwort new.
Das ruft eben diese besondere Methode auf, den Konstruktor.
In den meisten Polymer-Sprachen, Java, C Sharp, PHP,
wird das new vor den Klassennamen geschrieben.
Also Auto A gleich new Auto.
In anderen Polymer-Sprachen ist das anders,
wie zum Beispiel in Ruby.
Da mache ich Klassenname Punkt new.
Das heißt, da sieht der Methodenaufruf des Konstruktors
genauso aus wie bei allen anderen Methoden.
Das unterstreiche nochmal,
dass der Konstruktor tatsächlich nichts anderes ist
als eine besondere Methode.
Also warum soll die anders aufgerufen werden?
Warum soll ich dafür new benutzen,
wenn ich auch einfach die Syntax benutzen kann
wie für alle anderen Methoden?
Da haben sie sich bei Ruby ein bisschen Gedanken gemacht
und das ein bisschen optimiert, wie ich finde.
Ja, jetzt ist die Frage,
was packe ich denn in so einen Konstruktor rein?
Welche Parameter sollte ein Konstruktor bekommen?
Da sage ich immer,
ein Konstruktor sollte all die Parameter bekommen,
die nötig sind, um das zu erzeugende Objekt
in einem gültigen Zustand zu hinterlassen.
Der Konstruktor hat eine zentrale Aufgabe,
nämlich, dass man keine Objekte erzeugen kann,
die ungültig sind.
Wenn ich also zum Beispiel ein Auto habe
und es ist ganz wichtig, dass es einen Kilometerstand hat,
dann muss der Konstruktor diesen Kilometerstand
in einem gültigen Zustand hinterlassen
und nicht einfach so etwas sagen wie
Kilometerstand gleich minus 1,
sondern der muss dafür sorgen,
dass der Kilometerstand zum Beispiel mindestens 0 ist,
dass er also nicht negativ sein kann.
Das ist ganz wichtig,
damit ich eben nicht Objekte einfach wahllos erzeugen kann
und dann arbeite ich damit
und irgendwo später,
wenn ich das Objekt dreimal irgendwo durchgereicht habe,
tritt auf einmal ein Fehler auf,
weil irgendein Attribut in einem ungültigen Zustand ist,
zum Beispiel, weil der Wert 0 ist
oder weil der Wert negativ ist oder was auch immer.
Dafür sollte ein Konstruktor Sorge tragen.
Der sollte alle die Parameter reinbekommen,
die absolut notwendig sind,
um das Objekt korrekt zu erzeugen.
Ganz oft gibt es auch Attribute,
die ich nicht zwingend setzen muss,
die einfach auf den Standardwert gesetzt werden können.
Dafür sollte auch der Konstruktor dann Sorge tragen.
Dafür überlädt man den Konstruktor dann sehr häufig.
Wenn ich also sage,
es gibt einen Konstruktor,
der alle Werte, die notwendig sind, reinbekommt,
aber es gibt auch andere Konstruktoren,
die den ersten überladen
und dann bestimmte Werte einfach auf Default-Werte setzen,
wie zum Beispiel Kilometerstand auf 0,
aber eben nicht auf Null oder auf Minus 1
oder sonst irgendwas,
sondern in einem gültigen Zustand.
Das ist die Hauptaufgabe des Konstruktors.
Dann haben wir noch zwei völlig andere Fragen,
die damit erstmal wenig zu tun haben,
nämlich einmal, was sind Enumerations?
Enumerations, das sind Datentypen
mit einer endlichen Menge an erlaubten Werten.
Der Klassiker ist sowas wie Wochentag.
Wochentage gibt es genau sieben Stück,
das werden nicht mehr und auch nicht weniger
und die haben einen definierten Namen.
Und da will ich mir nicht jedes Mal merken,
wenn ich programmiere,
war jetzt Null Sonntag oder Montag?
Ich weiß es nicht mehr.
Stattdessen lege ich mir eine Enumeration an,
wo halt alle erlaubten Werte drinstehen,
also Montag, Dienstag, Sonntag bis Sonntag
und dann habe ich den Vorteil,
dass ich zum einen sprechende Namen benutzen kann
und nicht irgendwelche Int-Werte zum Beispiel,
die den Datentyp repräsentieren
und ich habe den Vorteil,
dass ich auch keine anderen
als die erlaubten Werte benutzen kann.
Ich kann dann nicht einfach,
wenn ich zum Beispiel Int als Wochentag benutze,
auf einmal eine Acht reingeben.
Den achten Wochentag gibt es nicht.
Das ist mit Enumerations ausgeschlossen,
weil ich eben nur eine Liste der erlaubten Werte definiere
und gar nichts anderes reingeben kann,
als in dieser Liste steht.
Und dann die letzte Frage,
welche Vorteile bieten Listen gegenüber Arrays?
Auch das eine recht allgemeine Frage.
Wenn wir jetzt an Listen denken,
an die generischen Klassen,
eine List of T zum Beispiel,
was hat das den Vorteil gegenüber Arrays?
Der zentrale Vorteil ist eigentlich,
dass sie redimensionierbar sind.
Arrays sind ja in ihrer Länge fix.
Wenn ich sie deklariere,
muss ich gleich sagen,
wie lang dieses Array ist.
Also zum Beispiel String 5,
dass also genau 5 String-Elemente dort drinstehen können
und ich kann den auch nicht verlängern
oder verkürzen oder sonst was.
Er bleibt fix.
Und bei Arrays ist es jetzt so,
dass sie im Speicher wirklich hintereinander angelegt werden.
Wenn ich also sage,
new String 5,
dann werden dort 5 String-Elemente im Speicher platziert
und zwar direkt aufeinanderfolgend im Speicher.
Das heißt,
die sind wirklich als ein Block im Speicher abgelegt.
Und da habe ich bei Listen den Vorteil,
die liegen nicht als ein Block da,
sondern die sind redimensionierbar.
Wie funktioniert das?
Jedes Element der Liste zeigt im Prinzip
auf seinen Vorgänger oder Nachfolger,
je nachdem, was man braucht.
Meistens auf beide.
Das heißt, ich habe eine doppelt verkettete Liste zum Beispiel,
in die Datenstruktur abtauchen wollen.
Und das funktioniert dann so,
dass ich das eine Listen-Element einfach irgendwo im Speicher ablege
und das hat dann halt einen kleinen Zeiger auf das nächste Objekt.
Und dieses Objekt kann irgendwo anders im Speicher liegen,
ist ganz egal,
denn der Vorgänger weiß ja, wo es liegt.
Das heißt, ich kann auch einfach die Liste erweitern
oder ich kann Elemente wieder rausnehmen
oder umsortieren oder was auch immer.
Das kann ich mit einer Liste verhältnismäßig einfach.
Im Array ist es halt so,
ein Block im Speicher und der ist dann da.
Und wenn ich den größer machen will,
dann geht das halt nicht.
Bei Listen geht das, weil sie redimensionierbar sind.
Eventuell sind sie sogar ein bisschen schneller als Arrays,
aber das kommt dann wieder auf die Promiersprache
und die Implementierung an,
welche Liste man nimmt,
ob es zum Beispiel eine Linked-List ist
oder eine Array-List,
die im Prinzip nichts anderes ist als ein Array,
bloß mit Listen-Funktionen.
Also da gehen wir jetzt ins Detail,
das interessiert dann tatsächlich in der Prüfung auch niemanden mehr.
Aber der zentrale Vorteil ist,
dass Listen eben redimensionierbar sind
und Arrays eben nicht.
Ja, das waren so ein paar Fragen zur Backgroundierung,
die mir nochmal so eingefallen sind,
die ich nochmal klären wollte.
Dann kommen wir nun zum nächsten großen Punkt,
nämlich der Fehlerbehandlung.
Und da steigen wir direkt ein mit dem allseits beliebten Thema,
was sind Exceptions?
Exceptions sind in vielen Promiersprachen heute Standard
für die Fehlerbehandlung.
Deswegen sollte man das durchaus erklären können,
was dahinter steckt.
Eine Exception beschreibt im Prinzip
eine Fehlersituation in einem Programm.
Und diese Fehlersituation, die kann herumgereicht werden.
Das heißt, ich muss diese Fehlersituation nicht dort behandeln,
wo sie auftritt,
sondern sie wird einfach immer weitergereicht
durch alle Aufrufer zurück,
bis sie irgendwo dann mal behandelt wird.
Und in dieser Fehlersituation stehen dann Informationen drin,
wie in welcher Zeile, in welcher Klasse
und wo auch immer dieser Fehler aufgetreten ist.
Das heißt, der gesamte Stack Trace steht da drin,
aber auch welcher Fehler genau es ist.
Und auch die Schwere teilweise lässt sich aus der Exception ablesen.
Also ob es ein, wie soll man sagen,
ein ganz schlimmer Fehler ist oder nicht ganz so schlimm.
Wichtig ist auf jeden Fall, dass sich diese Exception im Prinzip…
Also eine Exception ist einfach ein Objekt einer bestimmten Klasse
oder einer Klassenhierarchie.
In Java haben wir zum Beispiel wirklich die Klasse Exception,
von der ich erben kann.
Es gibt aber auch noch eine andere Klasse, Error zum Beispiel.
Und es gibt auch noch so ein Ding dazwischen,
das ist das Throwable und beide erben davon, von Throwable.
Also die Error-Klasse und die Exception-Klasse.
Ich weiß jetzt ehrlich gesagt nicht, ob Throwable ein Interface ist oder eine Klasse.
Auf jeden Fall hängen die in einer Hierarchie zusammen.
Und diese Exceptions, die kann ich jetzt mit einigen Schlüsselwörtern in der Promiesprache…
Ja, man hat dann so ein bisschen die Analogie wie so einen Ball,
den ich werfe und auffangen kann.
Und rund um diese Analogie drehen sich auch die ganzen Schlüsselwörter.
Und zwar gibt es dort die bekannten Schlüsselwörter try, catch, throw
und in Java noch die Besonderheit throws.
Und die gucken wir uns jetzt mal an.
Das try, catch, das umschließt einen potenziell fehlerhaften Code in meiner Promiesprache.
Ich könnte also sagen try und dann mache ich was, was eventuell fehlerhaft sein könnte,
wie z.B. connect-to-database.
Und bei diesem connect-to-database kann einiges schief gehen,
wie z.B. benutze mein Passwort falsch oder die Datenbank ist nicht erreichbar.
Und wenn das auftritt, dann wirft dieser Code, der diesen Fehler produziert, eben eine Exception.
Die wird dort geworfen und die kann ich dann in meinem catch-Block entsprechend auffangen.
Ich sage dann also, catch mal die, was auch immer, database-not-found-Exception
und dann kann ich darauf reagieren und diesen Fehler eben behandeln.
Wenn ich dann in dem aufgerufenen Code, der den Fehler produziert,
tatsächlich manuell eine Exception werfen möchte, dann ist das Schlüsselwort dafür throw.
Also wir reden hier die ganze Zeit von Java, aber in anderen Promiesprachen ist das sehr ähnlich,
in C Sharp z.B.
In Ruby heißen die anders, da heißen die z.B. race, um eine Exception zu werfen,
aber in den meisten gängigen Promiesprachen heißt es throw.
Und in Java kommt noch eine Besonderheit dazu, da gibt es nämlich die sogenannten checked Exceptions.
Dort kann ich Methoden kennzeichnen, als potenziell fehleranfällig
und das bedeutet, ich muss die Fehlerbehandlung beim Aufruf einbauen.
Ich kann diese Methode nicht einfach aufrufen, ohne ein try-catch drumherum
oder selber die Exception weiter zu werfen,
indem ich an meine eigene Methode nochmal wieder das throw dran schreibe.
Dieses Konzept der checked Exceptions, das ist Grundlage für viele tiefe Diskussionen,
ob das gut ist oder schlecht.
Man müsste wissen, dass es in Java eben so ist.
Ich kenne keine andere Promiesprache, wo es so umgesetzt ist,
aber Java hat das nun mal durchgezogen.
Das bedeutet, diese checked Exception ist etwas, was ich auffangen muss,
während die unchecked Exceptions eben solche sind, die auftreten können,
die ich aber nicht unbedingt auffangen muss.
Also zum Beispiel Laufzeitfehler, die ich auch gar nicht erwarten kann,
wie zum Beispiel die Festplatte ist voll oder der Speicher ist voll.
Das sind Dinge, die kommen so unregelmäßig vor,
auf die kann ich jetzt nicht bei jedem Fitzel-Methodenaufruf reagieren.
Wenn das auftritt, dann habe ich Pech gehabt,
dann kann ich vielleicht in irgendeinem globalen try-catch solche Sachen behandeln,
aber ich kann auch eigentlich nichts machen, wenn das auftritt.
Wenn die Festplatte voll ist, ja, was kann ich dann tun?
Weiß ich nicht.
Deswegen gibt es diese Unterscheidung in Java, einmal checked und unchecked.
Das wäre dann auch gleich die nächste Frage, die man da stellen könnte.
Also, was gibt es für Exceptions in Java?
Ich habe es gerade gesagt, checked und unchecked.
Und der Unterschied ist, dass die checked Exceptions gefangen werden müssen
und die unchecked Exceptions nicht.
Das wäre es dann auch schon zum Bereich Fehlerbehandlung.
Dann kommen wir jetzt zum letzten Punkt, nämlich zu den tiefergehenden Fragen.
Da habe ich auch noch mal einige Sachen zusammengestellt,
die ich nirgendswo anders unterbringen konnte, sage ich mal,
und die ich auch nicht um jeden Prüfling stellen würde.
Oder anders gesagt, wovon ich nicht ausgehen würde, dass jeder Prüfling sie beantworten könnte.
Das sind eher Fragen, die ich so einem Einser-Kandidat vielleicht stellen würde.
Und das geht dann los mit einer meiner Meinung nach noch ziemlich einfachen Frage,
aber trotzdem kann man das nicht voraussetzen, dass ihr das kennt.
Was ist Refactoring?
Refactoring, dafür habe ich mal eine ganz tolle Definition irgendwo gelesen.
Ich weiß leider nicht mehr wo.
Auf Deutsch die semantikinvariante Modifikation von Quelltext.
Das ist Refactoring.
Wenn man sich das mal auf der Zunge zergehen lässt.
Ja, semantikinvariant.
Semantik und Syntax hatten wir schon mal in einer früheren Podcast-Episode auseinandergenommen.
Semantik ist ja die Bedeutung meines Codes.
Und wenn etwas invariant ist, dann bedeutet das, dass sich das nicht ändert.
Das heißt, die Bedeutung ändert sich nicht.
Modifikation heißt ja bearbeiten.
Und Quelltext ist klar.
Das heißt, ich verändere den Quelltext, ohne die Bedeutung zu verändern.
Das ist Refactoring.
Und das ist auch eine schöne Definition, wenn man das mal ein bisschen anders formuliert.
Ich verändere den Quelltext, ohne das nach außen sichtbare Verhalten zu ändern.
Was bedeutet das?
Ich mache den Code schöner, aber ich ändere eben nicht, was er macht.
Zum Beispiel habe ich einen ganz tollen Algorithmus programmiert, der aber leider total langsam ist.
Und es ist meinetwegen ein Suchalgorithmus.
Dann, wenn ich den jetzt refaktorisiere oder refaktore, oder wie auch immer ich das nennen will,
dann verändere ich eben die interne Struktur dieses Codes.
Ich benenne vielleicht Variablen um.
Oder ich nutze einen anderen Algorithmus.
Oder weiß der Geier, ich extrahiere Methoden, um den Code lesbarer zu machen.
Solche Dinge.
Aber das führt nicht dazu, dass die Methode sich nachher unterschiedlich verhält,
nachdem ich dieses Refactoring gemacht habe.
Das heißt, genau wie vorher, wenn es ein Suchalgorithmus war,
wird es das Element in einem Array meinetwegen finden.
Genau wie vorher.
Egal, ob ich ein Refactoring mache oder nicht.
Das Verhalten ist unverändert.
Nur der Code selber, die interne Struktur wird verändert.
Meistens mit dem Ziel, um den Code besser lesbar zu machen für den Menschen.
Denn im Computer ist es eigentlich völlig wurscht, wie die Variablen heißen.
Aber der Programmierer, der den Code liest, der sollte ihn auch gut verstehen können.
Und dafür sind viele der Refactoring-Maßnahmen, die es so gibt,
ein paar hatte ich schon erwähnt, wie zum Beispiel Variablen,
unbenennen, Methoden extrahieren,
die sind eigentlich dazu da, um dem Programmierer, dem Leser des Codes,
das Leben einfacher zu machen.
Wichtig ist beim Refactoring, dass ich das nicht einfach so machen kann, sondern dass
ich normalerweise dafür Tests brauche. Automatische Tests, Unit Tests, um meinen
Code abzusichern. Ich will ja sicherstellen, dass die
Bedeutung sich nicht verändert. Das heißt, das nach außen sichtbare Verhalten
muss vorher und nachher gleich sein. Aber wie kann ich das sicherstellen? Nur durch
Tests. Nur indem ich ausprobiere, ob sich was verändert hat. Und dafür brauche ich
Unit Tests. Das heißt, Nummer eins, die Voraussetzung, um ein Refactoring
durchführen zu können, ist, ich muss automatische Tests haben. Denn wenn ich
die Struktur meines Codes verändere und mach dabei aus Versehen was kaputt,
das wäre schlecht. Das darf ja auf keinen Fall passieren. Und das kann ich nur
quasi beweisen, indem ich Tests habe, die ich vorher und nachher laufen lasse und die
sicherstellen, dass sich wirklich nichts verändert hat. Dann kommen wir zu einem
weiteren Stichwort, was man meiner Meinung nach heute durchaus erläutern
können müsste. Aber nicht jede Sprache kann das, deswegen würde ich es nicht
allgemein voraussetzen. Aber wenn ich zum Beispiel mit C-Sharp arbeite, dann ist
das Schlüsselwort Typ-Inferenz für mich schon eigentlich Voraussetzung, dass man
das erkennen, aber auch erklären können muss. Also was ist Typ-Inferenz?
Typ-Inferenz bedeutet, dass der Compiler den Datentyp einer Variable
automatisch ableiten kann, ohne dass ich ihn explizit angeben muss. Aber der
Datentyp ist nachher dann auch wirklich fix. Das hat also nichts mit dynamischer
Typisierung zu tun, sondern ich lasse den Datentyp einfach weg, aber meine
statisch typisierte Poemiersprache erkennt von alleine, welchen Datentyp es
denn dort eigentlich geben müsste. Und ab diesem Zeitpunkt ist das dann auch
der Datentyp. Als Beispiel. In C-Sharp haben wir das Schlüsselwort
War. Damit kann ich eben Variablen definieren, ohne den Typ anzugeben. Ich
kann also sowas machen wie War Test gleich in Anführungszeichen Hallo Welt.
Damit habe ich eine Variable Test definiert. Die Frage ist, welchen
Datentyp hat denn jetzt diese Variable? Der Compiler schaut jetzt einfach auf die
rechte Seite der Zuweisung und sieht, ah, Hallo Welt, ein String. Das heißt, diese
Variable muss wohl den Datentyp String haben und das erkennt der Compiler jetzt,
das inferiert er, das leitet er ab aus dem Wert, den ich da reinschiebe. Und ab
diesem Zeitpunkt hat die Variable jetzt auch wirklich den Datentyp String. Da kann
ich jetzt also nicht in der nächsten Zeile dann machen, ach, Test gleich 1. Das geht
nicht, denn Int und String passen nicht zusammen. Also ganz, ganz wichtig. Bedeutet
nicht, dass die Sprache dynamisch ist. Im Gegenteil, die ist immer noch sehr, sehr
statisch typisiert. Es macht mir bloß das Leben einfacher, weil ich eben nicht
diese Klassik habe wie in Java, wo ich immer links und rechts meine Variablen,
Entschuldigung, meine Datentypen anführen muss. Wie in der letzten Episode schon. Auto, BMW
gleich New Auto. Warum muss ich das Auto zweimal schreiben? Das ist total
überflüssig. Wenn ich sage, war BMW gleich New Auto, dann könnte der Compiler
eigentlich auch selber so intelligent sein, um zu sehen, ah, New Auto, ja, dann wird der
BMW wohl ein Auto sein und nicht auf einmal ein Baum oder sowas. Und das kann
eben der Compiler in vernünftigen Sprachen inferieren und das spart mir als
Entwickler einiges an Arbeit, denn ich muss eben meine ganzen Datentypen nicht
73 mal hinschreiben. Ja, dann noch eine weitere nette Frage, die ich durchaus
vielen Leuten stelle, weil fast jeder die eigentlich in seinem Code benutzt,
nämlich generische Klassen. Was sind das eigentlich? Und das Schöne ist, das können
die wenigsten erklären, obwohl man es tagtäglich benutzt. Dabei ist es eigentlich
recht einfach. Generische Klassen oder auch Generics, werden sie in Java genannt,
die stellen allgemeingültige Algorithmen zur Verfügung und zwar für
unterschiedliche Datentypen. Was bedeutet das jetzt? Klassisches Beispiel ist die
Liste, List of T in Java oder auch in C-Sharp. Das ist eine Klasse, die eine
ganz allgemeine Funktionalität bereitsteht, nämlich Listenverwaltung.
Also Elemente der Liste hinzufügen, löschen, sortieren, was auch immer die so
kann. Aber das Problem mit diesen Sprachen ist ja, dass sie statisch
typisiert sind. Das heißt, wenn ich jetzt eine Liste von
Strings haben will oder eine Liste von Integern oder eine Liste von Double, dann
muss ich die Klasse dreimal programmieren. Die eine arbeitet mit Int, die eine mit
Double, die eine mit String. Und das ist natürlich super aufwendig, obwohl die
Implementierung für alle diese Datentypen eigentlich identisch ist.
Der Liste ist dann völlig egal, ob ich eine Zahl speichere oder ein String. Solange
ich die zum Beispiel miteinander vergleichen kann, kann ich die Liste
sortieren. Und genau das ist der Sinn dieser generischen Liste. Ich stelle
diese Implementierung einmal bereit, völlig unabhängig vom konkreten
Datentyp, der in dieser Liste gespeichert wird. Aber ich muss eben in meinen
statischen Programmier-Sprachen dafür sorgen, dass die Liste typsicher ist, dass sie
statisch typisiert ist. Das heißt, ich muss jetzt sagen, wenn ich eine Liste erzeuge,
das ist eine List of String, damit ich eben nicht irgendwas anderes in die
Liste reinschieben kann als ein String. Das geht zwar, ich kann die Liste auch
einfach als List definieren und dann kann ich alles Mögliche da reinschieben, aber
in den meisten Fällen ist das nicht das, was ich will.
Denn dann muss ich ja beim Rausholen aus der Liste immer wieder ein Cast machen.
Und das wollen wir eigentlich nicht. Wir wollen eigentlich die Typsicherheit
haben, dafür nutzen wir diese Programmier-Sprachen. Und deswegen würde ich sowas
definieren wie List of String und weiß dann, da sind immer Strings drin. Und wenn
ich dann Int reinschiebe, dann gibt der Compiler mir den Fehler und sagt,
Moment mal, das geht nicht. Und damit habe ich eine bestimmte Art von Fehlern
ausgeschlossen, die ich sonst eventuell machen würde.
Also, wir stellen allgemeingültige Algorithmen dar und ich brauche diese
Generics aber nur in statisch typisierten Sprachen. Denn nur da kommt es auf diese
Typisierung an. Wenn ich in Ruby eine Liste programmiere, dann muss ich gar
nicht angeben, was für ein Datentyp da drin ist. Das kann die einfach selber die
Sprache, weil sie halt dynamisch ist. Wenn ich also mit diesen Elementen in der Liste
irgendwas machen will, dann wird halt zur Laufzeit geprüft, ob die alle Methoden
bereitstellen, die benötigt werden, um irgendwas damit zu tun. Und wenn es halt
nicht ist, dann gibt es einen Fehler. Aber es gibt eben nicht den Compiler, der
zur Compile-Zeit schon prüft, ob das der Fall ist.
Das heißt, generische Klassen, wichtiger Bestandteil statisch typisierter
Programmiersprachen, die stellen allgemeingültige Algorithmen bereit, und
zwar für unterschiedliche Datentypen. Das ist der zentrale Ansatz dabei.
Und in diesem Zusammenhang, gerade mit den Listen, kann man noch ein weiteres
schönes Thema ansprechen, nämlich die Co- und Contra-Varianz. Ich fange vielleicht
mit dem Beispiel an. In Java haben wir eine List of String und eine List of
Object. Jetzt denken sehr, sehr viele Entwickler, und ich gebe zu, ich gehörte
auch lange Zeit dazu, dass die List of String ja wohl von List of Object erben
wird. Denn String erbt ja auch von Object. Also warum sollte List of String nicht
von List of Object erben? Es ist ja eigentlich sinnvoll, dass das so ist.
Aber das ist nicht der Fall. List of String und List of Object stehen in
keiner Beziehung zueinander, insbesondere nicht in einer
Vererbungsbeziehung. Das ist nicht so, sondern es sind gleichrangige Klassen,
die sich nicht substituieren lassen. Das heißt, ich kann nicht einfach sagen, eine
List of Object gleich List of String. Das funktioniert nicht. Und das bedeutet,
dass diese beiden Listen nicht Co-Variant sind. Das ist jetzt genau die Frage, die
als letztes kommt, nämlich was ist Co- und Contra-Varianz? Und das ist etwas
tiefergehendes, das würde ich wirklich nicht unbedingt jeden Azubi fragen. Und
wer das beantworten kann, der hat ja quasi schon seine Eins sicher, so in der Art
und Weise. Aber wenn ich mir mal anschaue, List of String und List of Object, die
sind eben nicht Co-Variant. Das bedeutet, List of String erbt nicht von List of
Object. Und diese Co- und Contra-Varianz kann man erklären anhand von zwei
Beispielen. Das erste ist jetzt eben diese parametrische Polymorphie, das
heißt meine Generics, wo eben der Parameter-Datentyp, String oder Object,
in einer Vererbungsbeziehung steht, aber der Container drumherum, die Liste, eben
nicht in einer Vererbungsbeziehung steht. Wäre das der Fall, würde List of String
von List of Object erben, dann wären sie Co-Variant, weil die Vererbungsbeziehung
String Object die gleiche wäre wie List String List Object. Es geht also in die
gleiche Richtung. Wenn ich den umgekehrten Weg hätte, angenommen, List of
String und List of Object hätte ich, aber List of Object könnte ich für List of
String benutzen. Object und String stehen in einer Vererbungsbeziehung, aber die
Vererbungsbeziehung der beiden Listen stünde in genau der umgekehrten
Richtung. Also so nach dem Motto List of Object erbt von List of String, obwohl
String von Object erbt, also genau umgekehrt wie beim Typ, dann hätten wir
eine Contra-Varianz. Aber auch das ist nicht gegeben bei den Listen. Und
vielleicht an einem noch etwas einfacheren Beispiel, wie man das
erklären kann, das geht auch bei Methoden. Wenn ich eine Vererbungsbeziehung
habe und eine Methode in einer Subklasse überschreibe und diese Subklasse hat
oder diese Methode hat einen Parameter und dieser Parameter wird in der
Subklasse auf einen Datentyp verändert, der von dem Datentyp in der
Basisklasse erbt. Also als Beispiel, ich habe in meiner Basisklasse eine Methode
Fahre und die bekommt ein Auto und ich habe in meiner Subklasse eine Methode
Fahre, die bekommt kein Auto, sondern ein BMW und dieser BMW, der erbt von Auto. Wenn
das so wäre, dann habe ich eine Ko-Varianz. Wenn es umgekehrt wäre, in
meiner Basisklasse definiere ich eine Methode Fahre, die bekommt ein BMW und in
meiner abgeleiteten Klasse habe ich eine Methode Fahre, die bekommt ein Auto, dann
wäre ja die Vererbungsbeziehung genau umgekehrt, dann würde ich wieder von
Contra-Varianz sprechen. Es gibt auch noch das Dritte, nämlich die In-Varianz. Das
bedeutet, ich kann das gar nicht verändern. Das heißt, wenn ich oben Fahre-Auto
habe, dann habe ich meiner Subklasse auch Fahre-Auto und ich kann diesen
Parameter gar nicht verändern. Dann würde ich von In-Varianz sprechen.
Ja, das war noch mal das Highlight zum Schluss. Also ich erwarte nicht, dass
jeder das so im Detail erklären kann. Es ist schon ein sehr tiefer gehendes Thema,
aber wenn man sich so von anderen abgrenzen will und eine sehr gute Note
erreichen will, spricht nichts dagegen, wenn man das auch mal erklären kann, die
Ko- und Contra-Varianz. Okay, damit schließe ich jetzt aber nun meine Liste der
häufigen Fragen rund um die Programmierung in diesem Fall ab und
weise wie gewohnt zum Ende der Episode auf die Shownotes hin, die ihr wie immer
findet unter www.anwöchns-entwickler-podcast.de
für die 13. Episode. Ich würde mich wie immer über euer Feedback freuen, egal ob
positiv oder negativ. Schreibt mir einfach alles, was ihr mir zu sagen habt.
Gerne per Mail an mail.anwöchns-entwickler-podcast.de oder über das Kontaktformular auf der
Website. Ich würde mich auch sehr freuen, wenn ihr mich weiter empfehlt und ganz
besonders würde ich mich freuen über ein iTunes-Review. Könnt ihr einfach unter
www.anwöchns-entwickler-podcast.de erreichen die Seite und wenn ihr mir dort
ein ehrliches Feedback gebt, das würde mich sehr freuen, würde dazu beitragen,
dass dieser Podcast noch ein bisschen bekannter wird und ich noch mehr Azubis
bei ihrem Weg zum Fachinformatiker helfen kann. Wenn ihr wissen wollt, was bei
mir so passiert im Blog und auch im Podcast, könnt ihr euch gerne auch für
den Newsletter anmelden unter www.anwöchns-entwickler-podcast.de
slash Newsletter. Da bekommt ihr als kleines Dankeschön auch direkt eine
Liste für die häufigen Probleme, die es in der Projektdokumentation gibt mit
über 100 Punkten, die ihr mal durchgehen könnt, bevor ihr eure Projektdokumentation
abgibt und wird in Zukunft auch noch eine weitere Liste hinzukommen für den
Projektantrag und auch für die Projektpräsentation. Die bekommt ihr
sofort zugesendet, wenn ihr euch angemeldet habt und außerdem bekommt
ihr dann jede Woche von mir die neuen Infos, was auf der Website so passiert.
Auch den Link der Woche, den ich immer freitags veröffentliche, den bekommt ihr
dann schon vorab am Montag. So bleibt ihr einfach auf dem Laufenden, was
ich so veröffentliche und hoffe, dass es euch dann so ein bisschen auch hilft bei
eurer Prüfungsvorbereitung. Damit sage ich jetzt auch wirklich vielen, vielen
Dank fürs Zuhören und bis zum nächsten Mal!









Hallo Stefan,
vorab einmal vielen Dank für alle diese Informationen!
Meine betriebliche Projektarbeit bewegt sich ausschließlich im frontend, sprich ich verwende nur JavaScript sowie AngularJs.
Ich frage mich ob ich in irgendeiner weise trotzdem mit dem Thema Objektorientierung rechnen kann`?
Ich bin mir in diesem Bezug sehr sehr unsicher was mich dort für Fragen erwarten könnten, in dem Bezug auf das Thema „Programmierung“.
Liebe Grüße,
Chris
Bei uns gehört OO zur „Grundbildung“. Ob dein Prüfungsausschuss das auch so sieht, kann ich dir nicht sagen. Aber ich würde behaupten, dass OO ein wichtiges Prüfungsthema ist und jederzeit auch im Fachgespräch drankommen kann, auch wenn du nicht direkt in deinem Projekt damit gearbeitet hast. Du musst dich ja auch mit Datenbanken auskennen, wenn du im Projekt keine eingesetzt hast. Also würde ich mich darauf vorbereiten! Es gibt halt gewisse Inhalte, zu denen jeder Anwendungsentwickler etwas sagen können muss.
Allerdings solltest du dich auch unbedingt auf Fragen zu deinen verwendeten Technologien vorbereiten. Es gibt nichts Peinlicheres, als wenn Prüflinge Fragen zu ihrem eigenen Projekt nicht beantworten können! Das wären dann in deinem Fall vielleicht Callbacks, funktionale Programmierung, Futures oder was auch immer du an JavaScript-Features verwendet hast.
Einfach super, extrem hilfreich und informativ Danke dafür !
Mein Abschlussprojekt soll mit Drupal 8 umgesetzt werden. Aktuell fällt mir einfach auf wie viele „Kinderkrankheiten“ oder Inconveniences (Drush funktioniert nur auf Linuxbasierten OS, keine Importfunktion im Frontend, Maschinennamen aller Entities sind „quasi“ unveränderlich) das CMS mit sich bringt. Welche Tipps für das für Fachgespräch oder die Doku kannst du mir geben, (ich habe zwar schon mit PHP gearbeitet aber mit Drupal selber noch nie) denn aktuell tue ich mich aufgrund Lernkurve ein „wenig“ schwer.
Vielen Dank im Vorraus !
Die Frage ist jetzt etwas unspezifisch. Vielleicht hilft dir das: Was sind mögliche Fragen im Fachgespräch zu einem PHP-Projekt?