WEBVTT

00:10.260 --> 00:13.160
Hier habe ich jetzt die Negero nicht, also wenn das der Fall ist, ich

00:13.160 --> 00:15.980
habe immer noch das True in meinem Ergebnisregister liegen, dann

00:15.980 --> 00:20.480
springe ich zur Sprungmarke when, die steht hier unten, führe also s

00:20.480 --> 00:20.900
aus.

00:21.840 --> 00:25.480
Wenn nicht, dann gehe ich einfach wieder in die nächste Zeile, führe t

00:25.480 --> 00:28.760
aus und habe dahinter nochmal wieder eine Sprungmarke drinstehen und

00:28.760 --> 00:32.540
die hier sagt, springe zum Ende, denn ich will ja nicht noch

00:32.540 --> 00:33.660
zusätzlich s aus.

00:34.200 --> 00:37.420
Sie sehen schon, diese Goto-Programmiererei ist ein bisschen

00:37.420 --> 00:41.020
unübersichtlicher und deswegen hat man sie ja auch abgeschafft als das

00:41.020 --> 00:42.900
strukturierte Programmieren links.

00:43.080 --> 00:46.880
Aber das ist eben das, was dann im Detail passiert.

00:48.160 --> 00:51.080
Jetzt haben wir if gesehen, if else gesehen, wie sieht das Ganze bei

00:51.080 --> 00:51.820
einer Schleife aus?

00:52.200 --> 00:54.420
Können Sie sich jetzt vielleicht auch schon vorstellen, wenn ich jetzt

00:54.420 --> 01:00.940
so eine Schleife hätte, while, solange die Werte an a1 und a2 gleich

01:00.940 --> 01:06.180
sind, mache s und dann irgendwann mache t, würde man das in Assembler

01:06.180 --> 01:09.820
hinschreiben, dass man auch hier wieder die Bedingung mit mehreren

01:09.820 --> 01:10.680
Befehlen nachbaut.

01:10.760 --> 01:15.660
Man lädt erst a, vergleicht es auf Gleichheit mit dem Wert an a2,

01:16.180 --> 01:19.740
negiert wieder und sagt hier, wenn das also nicht der Fall ist, dann

01:19.740 --> 01:24.080
springe zum Ende und sonst wird ja immer der Befehlszähler einfach

01:24.080 --> 01:27.420
eins weitergesetzt, führe ich eben s aus und wenn ich dann s

01:27.420 --> 01:29.880
ausgeführt habe, habe ich hier wieder einen unbedingten Sprung, der

01:29.880 --> 01:32.900
mich direkt wieder oben zu dieser Marke while springen lässt.

01:33.340 --> 01:36.900
Also ich führe das hier immer aus, bis diese Bedingung mal irgendwann

01:36.900 --> 01:41.020
nicht mehr wahr ist, deswegen hier das not und ich dann an diesen

01:41.020 --> 01:45.740
bedingten Sprung end komme und zur Marke end auch springe.

01:47.080 --> 01:51.180
So sehen sie, hat ihr Programm ganz unten auf dem Prozessor keine

01:51.180 --> 01:54.620
expliziten Schleifen, keine expliziten Bedingungen mehr, sondern nur

01:54.620 --> 01:56.800
noch Vergleiche und Sprünge.

01:57.600 --> 02:00.200
Das ist eben dieses Funktionieren unter der Haube.

02:02.280 --> 02:05.180
Genau, kommen wir zurück zur Speichereinteilung und was da dann jetzt

02:05.180 --> 02:08.640
also bei Methoden aufrufen passiert, das war ja das, wo wir eigentlich

02:08.640 --> 02:10.460
hin wollten.

02:12.220 --> 02:15.520
Ich hatte ja eben schon gesagt, dass in diesem Datenbereich so ein

02:15.520 --> 02:19.780
Keller als Datenstruktur verwendet wird, um diese einzelnen Schachteln

02:19.780 --> 02:22.880
von Methoden zu speichern.

02:23.100 --> 02:26.320
Das steht hier jetzt nochmal, dass eben dieser Laufzeitkeller dafür da

02:26.320 --> 02:30.520
ist, die dynamischen Programmvariablen meines Programms zu speichern.

02:30.680 --> 02:33.000
Warum verwendet man hier die Datenstruktur Kellerspeicher?

02:33.000 --> 02:35.780
Naja, das liegt daran, dass ich ja immer wieder Unterprogramme

02:35.780 --> 02:38.940
aufrufen kann, also Methoden aufrufen kann und dann ja immer die

02:38.940 --> 02:42.360
aktuelle Methode, das ist, worauf mein Programm zugreifen kann in dem

02:42.360 --> 02:42.640
Moment.

02:43.040 --> 02:46.340
Deswegen macht es Sinn, dass das eben auf einem Keller ganz oben

02:46.340 --> 02:46.520
liegt.

02:46.600 --> 02:49.300
Alles darunter muss ich mir zwar merken, um nachher zurückzukommen,

02:49.400 --> 02:50.460
aber brauche ich in dem Moment nicht.

02:51.600 --> 02:54.540
Und ich kann dann so eben eine beliebige oder eben dynamische

02:54.540 --> 02:56.400
Verschachtelungstiefe dann erreichen.

02:57.360 --> 03:00.480
In dem Zusammenhang Laufzeitkeller bezeichnet man diese einzelnen

03:00.480 --> 03:03.720
Elemente des Kellerspeichers dann als Schachteln oder eben auf

03:03.720 --> 03:06.960
Englisch Frames, Stack Frames, Activation Records, verschiedene

03:06.960 --> 03:08.560
Anonyme finden Sie dafür.

03:09.420 --> 03:09.700
Genau.

03:11.000 --> 03:14.320
Die unterste Schachtel ist reserviert für statische Variablen, also

03:14.320 --> 03:15.440
einfach am Rande bemerkt.

03:15.960 --> 03:19.040
Und die weiteren Schachteln für den einzelnen Methodenaufruf

03:19.040 --> 03:22.760
beinhalten dann so Dinge, insbesondere wie die lokalen Variablen und

03:22.760 --> 03:25.200
die Rückkehradressen und auch noch ein paar Dinge mehr, die ich jetzt

03:25.200 --> 03:28.120
hier in der Vorlesung einfach ausgeblendet habe, um es nicht zu

03:28.120 --> 03:28.840
kompliziert zu machen.

03:30.280 --> 03:32.880
Wir haben hier rechts auch nochmal dieses Bild von dem Speicher mit

03:32.880 --> 03:36.200
auch verschiedenen Variablen für verschiedene Adressen.

03:36.580 --> 03:37.800
Schauen wir uns jetzt nochmal an.

03:39.560 --> 03:42.840
Ich habe also insgesamt meinen Speicher, der eben aus diesem Variablen

03:42.840 --> 03:46.460
-Teil, der als Kellerspeicher organisiert ist, unter Halde besteht.

03:46.460 --> 03:51.680
Und die Variablen oder die Adresse minvar gibt mir eben an, wo der

03:51.680 --> 03:55.340
Adressbereich meines Variablen-Speichers, also meines Kellerspeichers

03:55.340 --> 03:55.860
beginnt.

03:56.420 --> 04:00.100
Und der Pegel gibt mir ja an, wo dieser Speicherbereich wieder endet.

04:00.680 --> 04:02.780
Es gibt noch eine weitere Variable, nämlich den sogenannten

04:02.780 --> 04:07.420
Umgebungszeiger, also da im Bild dargestellt, hier beschrieben, was

04:07.420 --> 04:12.160
die Basisadresse für die lokalen Variablen in meiner aktuellen

04:12.160 --> 04:15.220
Schachtel habe, also von meiner aktuellen Methode habe.

04:15.220 --> 04:18.300
Da muss ich also nicht immer absolut drauf adressieren, die wären auch

04:18.300 --> 04:21.400
ein bisschen lang, die absoluten Adressen, sondern kann sogenannte

04:21.400 --> 04:23.240
relative Adressen verwenden.

04:23.320 --> 04:24.400
Genau, das steht unten gleich nochmal.

04:25.200 --> 04:28.180
Also, das Programm, Variable-Programm, besteht aus so mehreren

04:28.180 --> 04:29.240
Schachteln, wie wir es gesehen haben.

04:29.760 --> 04:32.080
Insbesondere interessiert uns immer die aktive Schachtel, die letzte,

04:32.640 --> 04:35.820
die da liegt, und auf diese zeigt dieser Umgebungszeiger.

04:36.940 --> 04:41.280
Der Kellerpegel gibt mal an, was die Adresse des obersten Elements in

04:41.280 --> 04:44.440
diesem Laufzeitkeller ist und wird dann im Detail auch noch verwendet

04:44.440 --> 04:44.820
für Zwischenergebnisse.

04:45.540 --> 04:47.520
Das hatten wir auch schon mal bei der Rekursion kurz besprochen.

04:48.040 --> 04:50.960
Wenn ich gerade zwei Werte miteinander addieren will, wird das

04:50.960 --> 04:54.840
Ergebnis auch erstmal auf diesen Kellerspeicher geschrieben und kann

04:54.840 --> 04:56.280
von da aus dann weitergearbeitet werden.

04:56.400 --> 04:59.060
Das ist also eine implizite lokale Variable, kann man sich vorstellen.

05:00.540 --> 05:03.140
Genau, und adressieren muss ich dann gar nicht, wie gesagt, mit der

05:03.140 --> 05:05.960
absoluten Adresse, die ein bisschen lang wäre, sondern kann es auch

05:05.960 --> 05:09.260
relativ zu diesen Adressvariablen hier machen.

05:09.440 --> 05:15.440
Also, ich kann sagen, eine Variable v liegt an der Stelle minvar plus

05:15.440 --> 05:19.340
relative Adresse von v und das ist dann eben, wie gesagt, kürzer zu

05:19.340 --> 05:19.780
adressieren.

05:19.920 --> 05:24.060
Dynamische Variablen kann ich adressieren mit Umgebungszeiger plus

05:24.060 --> 05:28.320
relative Adresse bei einer dynamischen Variable.

05:30.600 --> 05:31.040
Okay.

05:32.200 --> 05:34.400
Schauen wir in diese aktiven Schachteln oder überhaupt in diese

05:34.400 --> 05:37.580
Schachteln nochmal ein bisschen rein, um dann zu gucken, wie sieht

05:37.580 --> 05:41.540
denn das beim Aufrufen und Verlassen aus.

05:41.800 --> 05:45.440
Also hier sollen jetzt nochmal zwei Schachteln dargestellt sein.

05:46.220 --> 05:47.760
Wieder, es wächst von oben nach unten.

05:47.940 --> 05:50.140
Also das hier, hier oben würde noch irgendwas anderes vom

05:50.140 --> 05:51.160
Kellerspeicher drüber liegen.

05:51.160 --> 05:54.000
Wir schauen uns jetzt aber die obersten beiden Schachteln an.

05:54.080 --> 05:56.840
Es gibt die Schachtel des Aufrufers und dann darunter die Schachtel

05:56.840 --> 06:01.180
des Unterprogramms, die halt aufgebaut wird, wenn ein Unterprogramm,

06:01.260 --> 06:03.140
also eine Methode, aufgerufen wird.

06:03.460 --> 06:06.020
Der Kellerpegel zeigt immer auf die letzte, also auf das Ende der

06:06.020 --> 06:09.200
letzten Schachtel und der Umgebungszeiger zeigt auf unsere gerade

06:09.200 --> 06:10.400
aktive Schachtel.

06:11.440 --> 06:11.700
Genau.

06:12.220 --> 06:15.940
Was passiert jetzt also, wenn ich eine Methode aufrufe?

06:16.940 --> 06:20.100
Also als erstes, also das ist jetzt noch etwas vereinfacht, wenn Sie

06:20.100 --> 06:21.920
in das Buch von Groß gucken, sehen Sie noch ein paar mehr Schritte,

06:22.080 --> 06:25.540
aber für unsere, aus einfacher Sicht hier, kann man sich das mit

06:25.540 --> 06:27.660
diesen fünf Schritten überlegen.

06:28.900 --> 06:29.620
Was passiert?

06:30.300 --> 06:33.480
Haben wir hier dargestellt, als erstes wird der Kellerpegel um den

06:33.480 --> 06:36.120
Umfang der Schachtel, den ich brauche, erhöht.

06:37.720 --> 06:41.700
Dann kann ich die aktuellen Parameter hier zuweisen, das sind ja die

06:41.700 --> 06:44.740
Werte, mit denen ich meine Methode aufrufe und wie Sie wissen, ist das

06:44.740 --> 06:48.160
ja wie lokale Variablen dann aus Sicht der aufgerufenen Methode.

06:48.560 --> 06:51.520
Da muss also auch Speicher reserviert werden und die Werte müssen

06:51.520 --> 06:52.380
hineingeschrieben werden.

06:52.380 --> 06:57.000
Dann trage ich die Rückkehradresse in diese Schachtel ein, damit ich

06:57.000 --> 07:02.940
später weiß, wohin ich in meinem Programm-Code-Teil zurückspringen

07:02.940 --> 07:06.120
muss, also das ist die Rückkehradresse für den Befehlsteil meines

07:06.120 --> 07:06.680
Programms.

07:08.260 --> 07:10.980
Dann schreibe ich auch noch die lokalen Variablen rein,

07:11.080 --> 07:14.380
beziehungsweise der restliche Speicher ist für die lokalen Variablen

07:14.380 --> 07:17.200
reserviert, die sind dann ja noch nicht initialisiert.

07:17.200 --> 07:21.100
Dann setze ich den Umgebungszeiger auf diese Schachtel und kann dann

07:21.100 --> 07:21.960
losrechnen.

07:22.080 --> 07:26.400
Also dann kann der Methodenaufruf passieren, indem ich einen Sprung

07:26.400 --> 07:29.740
mache auf die erste Adresse meiner Methode in diesem Programm-Teil,

07:29.840 --> 07:30.780
also da, wo die Befehle sind.

07:30.840 --> 07:34.160
Und dazu brauchen wir jetzt eben auch diesen Konzept eines Sprungs.

07:36.260 --> 07:38.920
Dann kann die Methode abgearbeitet werden und wenn die Methode dann

07:38.920 --> 07:43.760
zum Ende kommt und dann aber zu so einem Return-Statement ankommt,

07:43.900 --> 07:46.860
dann muss ich eine Methode ja auch wieder verlassen.

07:47.320 --> 07:52.420
Das heißt, ich muss diese aktuelle Schachtel wieder zurückbauen oder

07:52.420 --> 07:53.920
eigentlich lässt man sie auch einfach stehen.

07:54.780 --> 07:55.860
Was muss ich denn machen?

07:55.960 --> 08:00.740
Ich muss erstmal ein eventuelles Ergebnis meiner Methode zuweisen an

08:00.740 --> 08:04.300
eine Variable, die dann für die aufrufende Methode dieses Ergebnis

08:04.300 --> 08:05.240
enthält.

08:05.300 --> 08:07.400
Die ist jetzt hier nicht dargestellt, die ist sozusagen hier so ein

08:07.400 --> 08:11.020
bisschen dazwischen, also als letztes in dieser Schachtel des

08:11.020 --> 08:11.600
Aufrufers.

08:14.020 --> 08:18.860
Dann weise ich die Rückkehradresse an ein Register R zu, dass ich da

08:18.860 --> 08:21.560
später noch darauf zugreifen kann, dass ich also weiß, wo ich gleich

08:21.560 --> 08:23.540
weiterlaufen muss.

08:24.040 --> 08:27.680
Dann setze ich den Umgebungszeiger zurück, weil ich dann gleich wieder

08:27.680 --> 08:30.760
bei der aufrufenden Methode bin.

08:31.380 --> 08:35.240
Und schließlich erniedrige ich den Kellerpegel wieder um so viel, wie

08:35.240 --> 08:37.300
die Schachtel groß war.

08:38.020 --> 08:42.420
Dann bleiben die Daten im Speicher sogar stehen, aber ich habe sie

08:42.420 --> 08:45.380
dadurch, dass ich die Referenzen darauf weggesetzt habe, für mich

08:45.380 --> 08:48.500
sozusagen deaktiviert und kann sie dann später wieder überschreiben.

08:49.940 --> 08:51.560
Das ist das, was ich für den Speicher tun muss.

08:51.700 --> 08:54.920
Die Schachtel sozusagen wieder zurückbauen, wie beim Atomkraftwerk,

08:55.040 --> 08:56.000
die geht ein bisschen schneller.

08:57.840 --> 09:01.920
Als letztes würde ich dann eben in meinem Programmteil einen

09:01.920 --> 09:06.240
unbedingten Sprung auf die eben gerade hier wieder gesicherte

09:06.240 --> 09:10.280
Rückkehradresse machen und dann also mit der Programmausführung an der

09:10.280 --> 09:14.500
Stelle, wo das die aufrufende Methode gerade war, dann fortsetzen.

09:16.900 --> 09:17.380
Genau.

09:17.660 --> 09:22.700
Und so viel auch schon zu dieser Frage, wie unter der Haube die

09:22.700 --> 09:25.120
Programme vom Prozessor eigentlich ausgeführt werden.

09:25.220 --> 09:27.560
Sie kennen wahrscheinlich aus Grundbegriffe der Informatik auch schon

09:27.560 --> 09:31.640
eine andere, auch detailliertere Betrachtung.

09:32.380 --> 09:34.920
Also genauer, was sind eigentlich die einzelnen Elemente des

09:34.920 --> 09:36.120
Prozessors und wie arbeiten die?

09:36.460 --> 09:38.460
Und wenn Sie dann in die Spezialvorlesung gehen, wird das alles

09:38.460 --> 09:41.400
nochmal viel komplizierter, aber für unsere Zwecke soll diese einfache

09:41.400 --> 09:43.400
Sicht jetzt erstmal reichen.

09:44.400 --> 09:46.280
Zusammengefasst nochmal, was haben wir gehört?

09:46.860 --> 09:50.840
Wir haben gesehen, dass es diese Halde gibt, reservierter Speicher zur

09:50.840 --> 09:55.640
Speicherung von Elementen zur Laufzeit bei Bedarf in insbesondere

09:55.640 --> 09:57.460
beliebiger Reihenfolge.

09:57.660 --> 10:00.320
Das wird in Java eben benutzt, um Objekte zu speichern.

10:00.320 --> 10:03.360
Und wir haben den Kellerspeicher kennengelernt, so eine Datenstruktur,

10:03.520 --> 10:07.540
die nach dem Last-In-First-Out-Prinzip arbeitet, wo ich also Elemente

10:07.540 --> 10:10.240
immer oben drauflegen kann, wenn ich es mir als Stapel vorstelle, oder

10:10.240 --> 10:11.120
unten reinlegen kann.

10:12.240 --> 10:15.880
Und das letzte Element immer wieder als erstes herausnehme.

10:15.960 --> 10:21.280
Deswegen Last-In-First-Out, der eben eingesetzt wird in Prozessoren.

10:21.280 --> 10:24.460
Und wir haben gesehen, dass der Speicher meines Programms besteht aus

10:24.460 --> 10:28.220
einerseits dem Programmbereich für die Befehle, wo dann eben dieser

10:28.220 --> 10:31.000
Befehlszähler mir gerade sagt, was als nächstes ausgeführt werden

10:31.000 --> 10:34.100
soll, einem konstanten Bereich für die Konstanten und einem

10:34.100 --> 10:35.700
Datenbereich für die Variablen.

10:36.060 --> 10:40.880
Und wir haben weiter gesehen im Detail, wie dieser Datenbereich

10:40.880 --> 10:44.560
aufgeteilt ist, nämlich eben in diesen Heap und den Kellerspeicher.

10:44.740 --> 10:46.760
Heap und Stack, Halde und Kellerspeicher.

10:47.780 --> 10:51.040
Wir haben uns auch diese drei Formen von Sprüngen angeschaut, um dann

10:51.040 --> 10:53.680
eben verstehen zu können, was beim Aufrufen und Verlassen von Methoden

10:53.680 --> 10:54.360
passiert.

10:54.500 --> 10:56.560
Da wären diese Sprünge dann ja eben relevant.

10:57.060 --> 11:00.560
Und schließlich haben wir uns dann dieses Aufrufen und Verlassen von

11:00.560 --> 11:01.640
Methoden auch angeschaut.

11:04.360 --> 11:04.840
Genau.

11:05.140 --> 11:05.820
So viel dazu.

11:06.800 --> 11:07.160
Gut.

11:10.640 --> 11:12.940
Kommen wir aus unserer Haube, unter der Haube wieder hervorgekrochen

11:12.940 --> 11:20.940
und kommen wieder zu was, ich will jetzt nicht sagen, relevanteren für

11:20.940 --> 11:21.640
die Abschlussaufgaben.

11:21.740 --> 11:25.000
Naja, aber was für relevanteren für die Abschlussaufgaben, nämlich die

11:25.000 --> 11:29.860
paar Hinweise, Tipps und Tricks, worauf ich bei der Programmierung mit

11:29.860 --> 11:32.180
Java besonders achten sollte.

11:32.920 --> 11:35.900
Hier nochmal unser Vorlesungsüberblick, was Sie alles schon können.

11:36.160 --> 11:39.920
Wir sind jetzt eben beim Bereich Methodik und hier das letzte Element

11:39.920 --> 11:41.480
der Best Practices.

11:41.720 --> 11:44.320
Wie gesagt, den oberen Teil hier haben Sie in den Tutorien dann auch

11:44.320 --> 11:45.180
besprochen.

11:46.060 --> 11:48.660
Was erwartet Sie jetzt in dieser Vorlesungseinheit?

11:49.060 --> 11:52.100
Wie gesagt, im Gegensatz zu den allgemeinen objektorientierten

11:52.100 --> 11:55.300
Prinzipien, die wir gesehen haben, schauen wir uns hier insbesondere

11:55.300 --> 11:58.920
Prinzipien des guten Programmierens in Java an, wobei insbesondere das

11:58.920 --> 12:02.740
erste auch recht allgemein gilt, aber hier jetzt auch mal speziell in

12:02.740 --> 12:04.120
Java Beispiele angeschaut.

12:06.420 --> 12:11.280
Was einerseits das Prinzip ist, Polymorphie lieber zu verwenden als

12:11.280 --> 12:15.340
Instance of, was nochmal eingeht auf die, oder ein weiteres Prinzip

12:15.340 --> 12:18.260
geht nochmal ein, auf die Unterscheidung zwischen gleich, gleich und

12:18.260 --> 12:19.900
Equals, haben wir auch schon mal angesprochen.

12:21.020 --> 12:23.700
Weiteres Prinzip ist, dass es oft sinnvoll ist, die

12:23.700 --> 12:27.560
Standardimplementierung von Equals zu überschreiben, dass man aber

12:27.560 --> 12:29.880
auch aufpassen muss bei der Implementierung von Equals, das ist dann

12:29.880 --> 12:30.580
der nächste Punkt.

12:31.280 --> 12:34.260
Hinweis, dass man Super Punkt Equals benutzen sollte bei

12:34.260 --> 12:39.500
Vererbungshierarchien, die Equals benutzen und worauf man achten

12:39.500 --> 12:46.420
sollte, wenn man Instance of in einer Equals Methode verwendet, was da

12:46.420 --> 12:47.820
nämlich nochmal die Spezifikation war.

12:48.580 --> 12:52.200
Genau, das Ganze sind also so Java spezifische Praxistipps gegenüber

12:52.200 --> 12:53.520
diesen allgemeinen Prinzipien.

12:54.680 --> 12:55.600
Was sind die Lernziele?

12:55.780 --> 12:59.320
Sie kennen die Best Practices für so oft auftretende Entscheidungen im

12:59.320 --> 13:02.120
Entwurf von Java Programmen, Sie können diese fünf vorgestellten

13:02.120 --> 13:05.140
Prinzipien auch anwenden, kennen den Einsatzzweck dieser Prinzipien

13:05.140 --> 13:10.320
und insgesamt der Zweck ist dann natürlich, dass Sie gut lesbaren und

13:10.320 --> 13:12.920
gut badbaren Code in Java schreiben können.

13:14.140 --> 13:17.360
Als Hintergrund, wenn Sie ein bisschen nachlesen wollen, ist hier

13:17.360 --> 13:19.760
dieses Buch Practical Java empfohlen.

13:20.120 --> 13:23.480
Das ist jetzt schon ein bisschen älter, aber hat dadurch, dass Java ja

13:23.480 --> 13:26.280
auch schon ein bisschen älter ist, jetzt nicht seine Relevanz

13:26.280 --> 13:26.860
verloren.

13:28.600 --> 13:30.360
Gut, gehen wir rein zu den einzelnen Prinzipien.

13:30.420 --> 13:35.440
Das erste Prinzip, erste Best Practice sagt, Polymorphie gegenüber

13:35.440 --> 13:37.900
Instance of zu bevorzugen.

13:37.900 --> 13:39.940
Was war nochmal Instance of?

13:40.380 --> 13:44.860
Das ist ja ein Befehl, eine Anweisung, die Ihnen zur Laufzeit

13:44.860 --> 13:49.280
bestimmt, zu welcher Klasse ein Objekt gehört, wird aber in Java auch

13:49.280 --> 13:52.600
gerne mal, weil es so einfach hinschreibbar ist, wahrscheinlich falsch

13:52.600 --> 13:58.120
eingesetzt und kann oder sollte in vielen Fällen durch Polymorphie

13:58.120 --> 13:59.240
ersetzt werden.

13:59.240 --> 14:03.880
Das bedeutet, schauen wir uns am besten anhand eines Beispiels für

14:03.880 --> 14:05.040
einen schlechten Entwurf an.

14:05.840 --> 14:11.280
Wir schauen uns ein Programm an, was erstmal ein Interface Employee

14:11.280 --> 14:12.400
für Mitarbeiter enthält.

14:13.160 --> 14:17.880
So Mitarbeiter haben ein Gehalt, also hier wird nur die Methode dafür,

14:18.460 --> 14:21.040
um das quasi Get Salary zu machen, definiert.

14:21.040 --> 14:24.420
Es gibt dann zwei Implementierungen von diesem Interface, nämlich

14:24.420 --> 14:28.840
einmal ein Manager, der eine Art von Mitarbeiter ist, der hat ein

14:28.840 --> 14:32.080
bestimmtes Gehalt, das kann man eben zurückgeben über diese Methode.

14:32.640 --> 14:38.020
Und es gibt eine zweite Klasse, die auch Employee implementiert,

14:38.620 --> 14:41.260
nämlich ein Programmierer, der auch ein bestimmtes Gehalt hat und da

14:41.260 --> 14:45.680
zusätzlich noch einen Bonus bekommen kann, wo ich eben zwei Methoden

14:45.680 --> 14:47.460
dann habe, Salary und Bonus.

14:48.100 --> 14:50.680
Jetzt kann man sagen, das Beispiel ist von Informatingern gemacht,

14:51.340 --> 14:52.580
wenn man sich die Zahlen so anschaut.

14:53.180 --> 14:54.220
Ob das denn immer so passt?

14:54.380 --> 14:55.880
Naja, sieht aber schön aus.

14:55.980 --> 14:57.120
Sieht so schöner aus, sagen wir so.

14:58.920 --> 15:00.980
Genau, was könnte ich jetzt mit so einem Programm machen?

15:01.680 --> 15:07.240
Ich könnte eben eine Klasse schreiben, die die Zahlungen von Gehältern

15:07.240 --> 15:10.200
an diese Mitarbeiter verwalten soll.

15:11.160 --> 15:14.460
Da bräuchte ich insbesondere dann eine Methode, die berechnet, wie

15:14.460 --> 15:17.940
viel ich denn jedem Mitarbeiter pro Monat zahlen muss.

15:18.840 --> 15:22.920
Und was ich da ja machen könnte, ist zu schreiben, naja, ich rufe halt

15:22.920 --> 15:25.900
für einen Mitarbeiter, der mir übergeben wird, jetzt mal vielleicht

15:25.900 --> 15:29.680
nur für einen Einzelnen, diese Methode Salary auf, um zu bestimmen,

15:29.820 --> 15:30.860
wie viel Geld er denn kriegen soll.

15:32.760 --> 15:35.540
Dann weiß ich aber ja, aha, wir haben ja auch spezielle Mitarbeiter,

15:35.680 --> 15:38.020
nämlich die Programmierer, die kriegen noch einen Bonus obendrauf auf

15:38.020 --> 15:38.540
ihr Gehalt.

15:39.340 --> 15:42.140
Dann könnte ich es so machen und sagen, naja, dann muss ich jetzt hier

15:42.140 --> 15:45.480
eine Abfrage machen, wenn der Employee ein Programmierer ist, dann

15:45.480 --> 15:48.940
rechne ich auf das Geld noch den Bonus obendrauf.

15:48.940 --> 15:52.220
Ich muss hier einen expliziten Cast machen, weil ich ja auch nicht

15:52.220 --> 15:55.580
weiß, dass Employee ein Programmierer ist, kann aber den Bonus dann

15:55.580 --> 16:02.200
abfragen und auf das Geld draufrechnen und dann die berechnete Summe

16:02.200 --> 16:03.280
zurückgeben.

16:06.150 --> 16:10.530
Ja, das Ganze könnte ich dann verwenden in einer Methode, ich kann

16:10.530 --> 16:13.350
mein Programm aufrufen und sagen, ich will so eine neue

16:13.350 --> 16:16.630
Abrechnungsklasse haben, ich habe einen Programmierer, einen Manager

16:16.630 --> 16:21.170
und, naja, die haben ja schon Standardwerte, brauche ich also gar

16:21.170 --> 16:25.730
nicht setzen, kann jetzt ausgeben, wie viel der Programmierer

16:25.730 --> 16:29.310
ausgezahlt bekommen soll und kann ausgeben, wie viel der Manager

16:29.310 --> 16:30.950
ausgezahlt bekommen soll.

16:32.110 --> 16:32.670
Genau.

16:35.590 --> 16:38.570
Ja, hier steht schon drüber, das ist ein Beispiel für einen schlechten

16:38.570 --> 16:39.130
Entwurf.

16:39.730 --> 16:40.130
Warum?

16:41.170 --> 16:47.290
Zum einen, weil dieses Instance of Arbeit des Java Laufzeitsystems

16:47.290 --> 16:50.110
bedeutet, hier muss also ein bisschen, das ist nicht so gut für die

16:50.110 --> 16:54.270
Performance, diese Abfrage zu machen, das aber eher am Rande mitmerkt,

16:54.590 --> 16:58.970
denn viel problematischer ist bei so einer Art, das umzusetzen, die

16:58.970 --> 17:00.170
Wartbarkeit Ihres Programms.

17:00.170 --> 17:03.290
Sie können sich jetzt fragen, was passiert denn jetzt, wenn Sie eine

17:03.290 --> 17:08.630
weitere Klasse hinzufügen wollen, zum Beispiel Executive, also

17:08.630 --> 17:14.070
irgendwie noch ein F, der vielleicht auch einen Bonus bekommt oder

17:14.070 --> 17:17.210
sonst noch eine spezielle Eigenart hat bei der Gehaltsberechnung,

17:18.050 --> 17:21.330
naja, man müsste natürlich um so eine Klasse hinzuzufügen, zum einen

17:21.330 --> 17:25.490
die Klasse selber hinzufügen, aber insbesondere auch noch diesen Teil

17:25.490 --> 17:29.590
zumindest überprüfen, ob hier bei der Gehaltsabrechnung für einen

17:29.590 --> 17:35.450
Chef, einen Executive, noch was Bestimmtes passieren muss.

17:35.690 --> 17:40.130
Also muss ich auch womöglich zur Calculate Payroll noch ein Instance

17:40.130 --> 17:43.670
of hinzufügen oder wie gesagt zumindest prüfen, ob für diesen neuen

17:43.670 --> 17:46.150
Fall, der hinzukommt bei der Erweiterung meines Programms auch noch

17:46.150 --> 17:47.950
irgendwas anderes gemacht werden muss.

17:47.950 --> 17:51.790
Und das, wie Sie ja auch schon ein bisschen mitbekommen haben sollten,

17:52.110 --> 17:55.930
ist eben gerade schlechter Entwurf, wenn ich eine Änderung nämlich an

17:55.930 --> 17:56.930
zwei Stellen machen muss.

17:57.070 --> 18:01.930
Es ist nicht closed für Änderungen, sondern eben hier problematisch.

18:01.930 --> 18:03.430
Was wäre besser?

18:05.710 --> 18:11.050
Hier zu machen, besser wäre es, hier auf die Polymorphie zu setzen und

18:11.050 --> 18:15.150
diese unterschiedliche Abfrage, was passieren soll, wenn ich einen

18:15.150 --> 18:18.290
Programmierer oder einen Manager habe, nicht hier in der Payroll

18:18.290 --> 18:22.850
-Komponente selber zu haben, sondern diese Unterscheidung sozusagen in

18:22.850 --> 18:25.110
die betroffenen Klassen hineinzuziehen.

18:25.110 --> 18:27.590
Das würde jetzt, also eine Lösung an dieser Stelle wäre jetzt zu

18:27.590 --> 18:30.550
sagen, naja, dann erweitere ich das Interface auch um diese Methode

18:30.550 --> 18:34.270
Bonus, erweitere auch noch den Manager, dass der eben keinen Bonus

18:34.270 --> 18:38.210
bekommt und dann habe ich hier ein bisschen mehr Code, obwohl der ist

18:38.210 --> 18:40.570
ja nicht komplex, dann habe ich aber den Vorteil, dass ich bei meiner

18:40.570 --> 18:43.430
Gehaltsabrechnung einfach nur sagen kann, für einen Employee, den ich

18:43.430 --> 18:46.670
bekomme, für die Frage, was ich ihm auszahlen soll, rechne ich halt

18:46.670 --> 18:49.970
die Salary plus den Bonus und muss jetzt hier keine Fallunterscheidung

18:49.970 --> 18:52.070
hinsichtlich der Typen mehr machen.

18:52.070 --> 18:55.810
Wenn ich jetzt eine neue Klasse Executive hinzufügen würde, müsste ich

18:55.810 --> 18:58.350
auch da nur die Klasse hinzufügen, diese beiden Methoden

18:58.350 --> 19:01.210
implementieren und müsste diese Methode, und das ist eben das Schöne,

19:01.510 --> 19:02.430
nicht nochmal anfassen.

19:02.530 --> 19:05.810
Sie würde einfach weiter funktionieren durch Polymorphismus und

19:05.810 --> 19:06.710
dynamische Bindung.

19:08.890 --> 19:13.670
Okay, das ist also das erste Prinzip, warum oder das Polymorphie

19:13.670 --> 19:18.850
oftmals im Instance Of vorzuziehen ist, was Sie hier an der Stelle auf

19:18.850 --> 19:21.870
jeden Fall mitnehmen sollten, ist, immer wenn Sie Instance Of

19:21.870 --> 19:25.330
hinschreiben, fragen Sie sich selber nochmal, ob das jetzt wirklich

19:25.330 --> 19:28.270
sein muss, oder überlegen Sie mal, wie könnte ich das ersetzen, wie

19:28.270 --> 19:30.430
könnte ich die Funktionalität, die ich hier eigentlich gerade so

19:30.430 --> 19:34.270
nachbasteln will, eigentlich besser in die Klassen meiner Objekte

19:34.270 --> 19:34.810
selber hineinziehen.

19:34.810 --> 19:38.530
Das sollte eigentlich immer das sein, dass Sie, immer wenn Sie

19:38.530 --> 19:40.330
Instance Of schreiben, erstmal einen Schreck kriegen und überlegen,

19:40.410 --> 19:41.170
wie könnte ich es besser machen.

19:42.750 --> 19:45.610
Genau, zweiter Prinzip, Unterscheidung zwischen diesem Gleich-Gleich

19:45.610 --> 19:49.250
-Vergleich, klingt immer ein bisschen blöd, aber egal, und Equals.

19:50.790 --> 19:53.990
Da fragt man sich, also gerade als Anfänger in der Programmierung, was

19:53.990 --> 19:54.590
ist da der Unterschied?

19:54.870 --> 19:55.890
Wir haben es ja auch schon mal besprochen.

19:57.390 --> 20:00.470
Kann ich nicht alles mit Gleich-Gleich-Vergleichen, wo brauche ich

20:00.470 --> 20:01.690
Equals, was ist da der Unterschied?

20:01.690 --> 20:04.110
Ich dachte, wir hatten es ja schon mal angesprochen, deswegen

20:04.110 --> 20:06.350
vielleicht hier ein bisschen schneller, aber trotzdem, weil das

20:06.350 --> 20:08.510
einfach so ein wichtiger Punkt und so ein typischer Fehler ist,

20:08.830 --> 20:11.430
besprechen wir es jetzt nochmal in anderen Worten.

20:12.510 --> 20:13.770
Also, was wäre hier das Beispiel?

20:14.750 --> 20:18.190
Wir stellen uns vor, wir haben so eine Klasse Test, die Zahlen

20:18.190 --> 20:20.370
miteinander vergleichen soll und wir gucken uns hier mal zwei

20:20.370 --> 20:21.150
verschiedene Fälle an.

20:21.150 --> 20:24.850
Einerseits habe ich zwei Integer-Variablen a und b, setze ich auf den

20:24.850 --> 20:29.850
Wert 10 und gebe dann aus, ob die gleich sind im Sinne von Gleich

20:29.850 --> 20:30.190
-Gleich.

20:30.550 --> 20:34.850
Ich gebe das Ergebnis von diesem Vergleich a Gleich-Gleich-b aus.

20:35.850 --> 20:37.690
Das ist das eine, was ich jetzt mal testweise mache.

20:37.770 --> 20:41.390
Das andere ist, dass ich zwei Integer-Objekte anlege über diese

20:41.390 --> 20:44.450
Objekte, die primitive Datentypen-Kapseln, haben wir ja schon mal

20:44.450 --> 20:44.770
gesprochen.

20:45.270 --> 20:49.990
Die nennen wir Ia und Ib, haben den gleichen Wert und mache auch da

20:49.990 --> 20:54.710
wieder die Ausgabe Ia Gleich-Gleich-b, das Ergebnis davon möchte ich

20:54.710 --> 20:55.110
ausgeben.

20:56.770 --> 20:59.850
Was dabei rauskommt, könnt ihr sich vielleicht denken.

21:00.130 --> 21:02.930
Das erste macht es so, wie man es erwarten würde.

21:03.350 --> 21:06.050
a Gleich-Gleich-b ist wahr, es handelt sich hier um primitive

21:06.050 --> 21:09.670
Datentypen und da kann ich eben mit diesem Gleich-Gleich-Operator den

21:09.670 --> 21:13.990
Inhalt der Variablen in der Speicherzelle, in dem Gefäß vergleichen

21:13.990 --> 21:17.270
und in den beiden Gefäßen a und b steht jeweils 10 drin und die sind

21:17.270 --> 21:18.670
halt gleich, ich bekomme ein True heraus.

21:19.290 --> 21:23.930
Wenn ich den zweiten Teil mir anschaue, bekomme ich das Ergebnis Ia

21:23.930 --> 21:25.610
Gleich -Gleich-ib ist false.

21:26.250 --> 21:27.990
Wie gesagt, hatten wir auch schon mal angesprochen.

21:28.610 --> 21:33.150
Und ist das Gleich-Gleich eben nur direkt vergleicht, was steht in den

21:33.150 --> 21:37.210
Variablen in diesen Gefäßen drin und bei Objekten steht da ja nicht

21:37.210 --> 21:40.010
der Wert selber des Objektes irgendwie drin oder irgendein Attribut,

21:40.010 --> 21:43.990
sondern da steht erstmal die Adresse, wo das Objekt im Heap

21:43.990 --> 21:44.890
gespeichert ist.

21:45.670 --> 21:49.150
Und wenn ich dann zwei Integer-Objekte anlege, liegen die an

21:49.150 --> 21:53.650
verschiedenen Stellen im Heap, das heißt, dieses Inhalt der Variablen

21:53.650 --> 21:58.530
Ia ist nicht gleich des Inhalts der Variablen Ib, in dem Sinne, dass

21:58.530 --> 22:00.890
die Adressen, die da jeweils drin liegen, eben nicht gleich sind.

22:00.890 --> 22:02.450
Deswegen bekomme ich das Ergebnis false.

22:04.030 --> 22:06.070
Was könnte ich jetzt noch mal machen?

22:06.330 --> 22:08.370
Weiterer Test, ich klappe ihn vielleicht mal gleich auf.

22:17.100 --> 22:17.520
Genau.

22:18.040 --> 22:19.580
Achso, genau, das wäre jetzt nochmal das Detaillierte.

22:19.760 --> 22:22.700
Also, es liegt tatsächlich eigentlich bei Ia und Ib auch der Wert 10

22:22.700 --> 22:26.820
dahinter, selbst wenn ich ihn mit dem automatischen String

22:26.820 --> 22:29.940
-Konvertierung ausgebe, aber trotzdem bekomme ich eben false aus dem

22:29.940 --> 22:30.720
eben genannten Grund.

22:31.360 --> 22:32.580
Ja, warum ist das so?

22:34.460 --> 22:38.180
A und B sind vom Typ Integer, sind primitive Datenobjekte und werden

22:38.180 --> 22:40.540
eben, ich hatte es ja auch gerade schon erklärt, direkt über die

22:40.540 --> 22:42.400
Inhalte dieser Variablen verglichen.

22:43.280 --> 22:46.800
Ia und Ib sind vom Typ Integer, sind eigentlich Objektreferenzen und

22:46.800 --> 22:50.300
gleich gleich vergleicht also die Adressen der Objekte, also

22:50.300 --> 22:52.980
vergleicht die Objektreferenzen.

22:54.040 --> 22:56.740
Okay, schön und gut, man weiß also gleich gleich lieber nicht auf

22:56.740 --> 23:01.180
Objekten verwenden, außer ich will wirklich wissen, ob die

23:01.180 --> 23:03.740
Objektidentität gleich ist, aber dann stellt sich die Frage, wie

23:03.740 --> 23:06.960
vergleiche ich denn jetzt gespeicherte Werte in Objekten?

23:07.280 --> 23:11.880
Da kommt eben Equals ins Spiel, was einen sogenannten semantischen

23:11.880 --> 23:16.020
Vergleich von Objekten macht, ob die in irgendeiner Form, wie es für

23:16.020 --> 23:18.420
mein Programm Sinn macht, gleich sind.

23:20.240 --> 23:23.520
Entsprechend, oder was heißt entsprechend, aber Equals ist eine

23:23.520 --> 23:27.520
Methode und das kann ich dann auch tatsächlich nur zum Vergleich von

23:27.520 --> 23:30.640
Objekten verwenden, nicht zum Vergleich von primitiven Datentypen, da

23:30.640 --> 23:32.900
muss ich weiterhin dieses gleich gleich benutzen.

23:34.300 --> 23:37.280
Okay, schauen wir uns ein weiteres Beispiel an, also wir haben das,

23:37.400 --> 23:40.540
was wir eben gesehen haben, diese detaillierten Ausgaben, wir haben

23:40.540 --> 23:44.980
jetzt hier noch ein paar Floats dabei, aber überhaupt erstmal meine

23:44.980 --> 23:48.660
normalen Werte, primitiven Datentypen vergleiche ich weiterhin mit

23:48.660 --> 23:52.280
gleich gleich und die Objekte vergleiche ich mit Equals.

23:52.280 --> 23:55.940
So erstmal die Idee, das würde auch funktionieren.

23:56.280 --> 23:58.280
In diesem weiteren Beispiel haben wir jetzt noch eine Änderung

23:58.280 --> 24:01.120
gemacht, dass wir nämlich gesagt haben, naja, wir versuchen jetzt mal

24:01.120 --> 24:04.120
einen Integer mit einem Float zu vergleichen, beide setzen wir auf den

24:04.120 --> 24:06.300
Wert 10, also einmal 10,0, einmal 10.

24:06.300 --> 24:08.980
Und wenn ich dann mit gleich gleich vergleiche, kommt tatsächlich

24:08.980 --> 24:12.280
auch, hier unten steht schon gleich, ähm, du raus.

24:12.960 --> 24:16.960
Machen wir mal was ähnliches, wir machen zwei Objekte, Integer, eins

24:16.960 --> 24:20.880
Integer mit dem Wert 10, ein Float mit dem Wert 10, haben jetzt ja

24:20.880 --> 24:25.900
gelernt, dass wir Equals benutzen sollten, rufen also Equals, ähm, auf

24:25.900 --> 24:29.340
diesen beiden Objekten auf, einmal so rum, einmal anders rum, also

24:29.340 --> 24:32.840
einmal auf dem einen Objekt, mit dem anderen Objekt als Parameter und

24:32.840 --> 24:33.300
umgekehrt.

24:34.820 --> 24:37.480
Und erhalten wieder Faults an der Stelle.

24:38.460 --> 24:40.540
Also wenn wir jetzt, wir haben jetzt den Fall übersprungen, wo wir

24:40.540 --> 24:42.580
beides auf Integer gelassen hätten, dann hätte es auch funktioniert,

24:42.680 --> 24:43.820
dann wäre ich zugekommen.

24:44.180 --> 24:46.580
Wenn man jetzt aber anfängt, hier das eine zu dem Float zu machen,

24:46.700 --> 24:50.500
obwohl sie ja quasi die gleiche Zahl enthalten, logisch, ähm, bekommen

24:50.500 --> 24:53.060
wir hier wieder Faults heraus.

24:54.280 --> 24:56.360
Ja, warum ist das so?

24:56.520 --> 24:59.980
Also erstmal auf der primitiven Seite funktioniert dieser Vergleich 10

24:59.980 --> 25:02.660
und 10 implizit schon, weil an dieser Stelle, wo ich den Vergleich

25:02.660 --> 25:05.880
mache, so eine erweiternde Primitivkonvertierung durchgeführt wird,

25:06.000 --> 25:09.120
also der Integer auch in den Float überführt wird und dann erst

25:09.120 --> 25:10.700
verglichen wird und dann sind sie eben gleich.

25:11.360 --> 25:17.660
Ähm, bei der Equals-Implementierung von, ähm, ja, von diesen Klassen

25:17.660 --> 25:21.540
Integer und Float ist es aber so, und das gehört auch zu dem Vertrag

25:21.540 --> 25:25.180
der Equals-Methode, dass Objekte, oder zumindest typisch so gemacht,

25:25.260 --> 25:28.540
nicht immer, dass Objekte unterschiedlicher Klassen, äh,

25:28.660 --> 25:31.700
typischerweise nicht als gleich, äh, bewertet werden.

25:31.820 --> 25:34.040
Kann man sich auch irgendwie vorstellen, die Klasse selber ist ja auch

25:34.040 --> 25:38.100
eine Art Eigenschaft von meinem Objekt und die ist eben nicht gleich.

25:38.100 --> 25:41.080
Also typischerweise implementiert man Equals so, dass Objekte

25:41.080 --> 25:43.960
unterschiedlicher Klassen nicht gleich sind.

25:44.040 --> 25:47.060
Und deswegen komme ich hier also, bekomme ich Falls raus, weil ja die

25:47.060 --> 25:50.900
Typen dieser beiden Objekten nicht, Objekte nicht gleich sind.

25:53.440 --> 25:53.840
Genau.

25:54.140 --> 25:56.300
Da müsste ich also, was ich jetzt hier machen könnte, wäre, wenn ich

25:56.300 --> 25:58.760
jetzt wirklich diese beiden Zahlen vergleichen will, ähm, mir den

25:58.760 --> 26:03.080
Wert, also entweder explizit konvertieren, in das eine auch in einen

26:03.080 --> 26:06.960
Float, oder, ähm, mir die primitiven Werte herausholen und dann

26:06.960 --> 26:07.420
vergleichen.

26:07.480 --> 26:10.260
Das wären so verschiedene Möglichkeiten, wie ich den Vergleich, so wie

26:10.260 --> 26:12.080
ich ihn wahrscheinlich hier haben wollte, hinbekomme.

26:14.260 --> 26:15.920
Okay, bleiben wir bei Equals.

26:16.380 --> 26:19.120
Ähm, wir haben jetzt also gesehen, was der Unterschied zwischen

26:19.120 --> 26:20.300
gleich, gleich und Equals ist.

26:20.800 --> 26:25.460
Und, ähm, dass auch bei einigen Klassen, die bei Java eben schon

26:25.460 --> 26:28.500
vorgefertigt da sind, wie Integer und Float, diese Equals-Methode auch

26:28.500 --> 26:29.680
schon implementiert ist.

26:29.680 --> 26:33.260
Wenn ich jetzt eigene Typen definiere, also eigene Klassen schreibe,

26:33.560 --> 26:35.780
dann kann ich auch die Standard-Implementierung von Equals

26:35.780 --> 26:39.320
überschreiben, beziehungsweise in vielen Fällen sollte ich das auch

26:39.320 --> 26:39.640
tun.

26:40.380 --> 26:42.120
Schauen wir uns das folgende Beispiel an.

26:42.440 --> 26:45.380
Angenommen, wir haben ein System, was irgendwie Golfbälle verwalten

26:45.380 --> 26:45.680
soll.

26:46.120 --> 26:47.200
Hier als Beispiel.

26:48.200 --> 26:52.900
Und wir wollen es jetzt haben, also ein Golfball hat Attribute, die

26:52.900 --> 26:56.580
Marke, vielleicht noch so eine Art Modell, irgendwie Kompressionswert.

26:56.580 --> 26:58.940
Spiel ich kein Golf und weiß eigentlich nicht, was das bedeutet, aber

26:58.940 --> 26:59.180
egal.

27:00.060 --> 27:03.880
Ähm, ich habe einen Constructor, der diese initialisiert und ich habe

27:03.880 --> 27:07.140
Methoden, die mir diese Attribute zurückgeben.

27:07.880 --> 27:10.820
Die sich auch getBrand und getMake nennen können, aber gut.

27:11.540 --> 27:13.700
Und hier würde ich es ja bei solchen, bei so einem System

27:13.700 --> 27:17.040
wahrscheinlich haben wollen, dass zwei Golfbälle dann gleich sind,

27:17.120 --> 27:20.040
wenn alle Attribute, diese drei Attribute genau den selben Wert haben.

27:21.340 --> 27:25.660
Wenn ich jetzt also, ähm, ja, irgendwie ein Warenhaus habe, wo diese

27:25.660 --> 27:29.800
Golfbälle verwaltet werden, könnte ich jetzt also sagen, okay, ich

27:29.800 --> 27:34.100
habe zwei Golfbälle, die ich hier initialisieren kann mit den gleichen

27:34.100 --> 27:34.760
Attributen.

27:34.760 --> 27:37.100
Und ich habe jetzt ja schon gelernt, aha, ich darf nicht gleich gleich

27:37.100 --> 27:40.960
benutzen, um die zu vergleichen und will dann ein VAR heraus haben,

27:41.100 --> 27:46.620
sondern ich verwende equals und sage dann Golfball 1 equals Golfball

27:46.620 --> 27:46.980
2.

27:47.340 --> 27:51.220
Wenn ja, dann gebe ich aus, Ball 1 equals Ball 2, wenn nicht, gebe ich

27:51.220 --> 27:53.220
aus, ist es nicht gleich.

27:53.740 --> 27:57.720
Ziel wäre, irgendwie zu, ein Programm zu haben, wo eben zwei Golfbälle

27:57.720 --> 28:00.540
gleich sind, wenn alle Attribute gleich sind, was jetzt hier der Fall

28:00.540 --> 28:00.720
ist.

28:00.720 --> 28:02.680
Was wäre hier aber die Programmausgabe?

28:04.100 --> 28:06.980
Ball 1 does not equal Ball 2.

28:07.920 --> 28:08.080
Hm.

28:09.940 --> 28:12.620
Ja, dabei haben wir ja eigentlich hier jetzt so aufgerufen, dass die

28:12.620 --> 28:13.900
Attribute eigentlich gleich sind.

28:16.580 --> 28:18.480
Warum ist das jetzt hier wieder so?

28:19.100 --> 28:21.800
Da muss man sich wieder anschauen, was man hier jetzt eigentlich bei

28:21.800 --> 28:23.860
dieser Equals-Methode aufruft.

28:23.860 --> 28:26.400
Und wir haben jetzt in unserer Klasse Golfball die Equals-Methode noch

28:26.400 --> 28:29.480
nicht selber implementiert, das heißt, wir rufen die Implementierung

28:29.480 --> 28:33.520
einer Superklasse, einer Oberklasse auf, das wäre hier Object, und die

28:33.520 --> 28:36.200
passt eben nicht zu dem, was wir hier eigentlich haben wollen.

28:37.880 --> 28:41.400
Warum funktioniert die Equals im Beispiel vom Prinzip eben, also mit

28:41.400 --> 28:43.380
den Integers, hier aber nicht.

28:44.780 --> 28:50.800
Im Beispiel mit den Integers hatten diese Klassen, IntegerFloat und so

28:50.800 --> 28:53.720
weiter, ihre eigene Implementierung der Equals-Methode, nicht nur die

28:53.720 --> 28:57.060
von Object, und hier bei unserem Golfball hatten wir in dem Beispiel,

28:57.140 --> 28:59.960
wo wir gerade standen, eben noch keine eigene Implementierung der

28:59.960 --> 29:00.640
Equals -Methode.

29:00.640 --> 29:01.780
Und was passiert dann?

29:01.880 --> 29:04.680
Es wird die Implementierung der Oberklasse verwendet, das ist die

29:04.680 --> 29:08.660
Implementierung, die in der Klasse Object schon vorgegeben ist.

29:08.940 --> 29:10.880
Kann man jetzt immer noch sagen, ja, und warum tut die nicht das, was

29:10.880 --> 29:11.360
sie soll?

29:12.300 --> 29:14.220
Ah, die gucken wir uns mal an.

29:15.060 --> 29:18.300
Die Equals-Methode der Klasse Object macht nichts anderes als das

29:18.300 --> 29:18.500
hier.

29:19.220 --> 29:21.900
Sie vergleicht wieder mit GleichGleich, ob die beiden Objekte gleich

29:21.900 --> 29:22.080
sind.

29:23.480 --> 29:25.840
Also, das wollten wir jetzt hier an der Stelle nicht haben, also das

29:25.840 --> 29:28.880
heißt, Equals, der Objekt-Methode macht dann doch wieder das gleiche

29:28.880 --> 29:31.780
wie GleichGleich, es vergleicht einfach, ob die Referenzen der zwei

29:31.780 --> 29:33.060
Objekte gleich sind.

29:33.720 --> 29:36.820
Macht auch von daher Sinn, Object ist so eine allgemeine Klasse, was

29:36.820 --> 29:39.760
soll man da groß anderes vergleichen können.

29:40.780 --> 29:44.180
Das heißt, einfach nur Equals zu benutzen für eigene Klassen bringt

29:44.180 --> 29:46.720
mich noch überhaupt nicht weiter, keinen Schritt weiter, als

29:46.720 --> 29:48.160
GleichGleich zu benutzen.

29:48.580 --> 29:52.500
Was ich noch machen muss, ist es eine eigene Implementierung von

29:52.500 --> 29:55.420
dieser Equals-Methode zu schreiben und damit diese Standard

29:55.420 --> 29:57.640
-Implementierung zu überschreiben.

29:58.360 --> 30:01.520
Nehmen also unsere Klasse Golfball her, der Rest bleibt eben gezeigt

30:01.520 --> 30:05.120
und fügen eine neue Methode Equals hinzu.

30:05.120 --> 30:06.540
Was machen wir da?

30:06.800 --> 30:09.860
Naja, wir prüfen erstmal, ob die Referenz schon gleich ist, das ist

30:09.860 --> 30:11.720
nämlich viel billiger, das zu tun, als den Rest.

30:12.220 --> 30:15.260
Den kann man das mal gleich probieren, wenn ja, also wenn ich das

30:15.260 --> 30:18.340
gleiche Objekt hier als Parameter und als this habe, dann kann ich

30:18.340 --> 30:19.060
true zurückgeben.

30:19.840 --> 30:23.320
Wenn nicht, dann prüfe ich, ob das übergebene Objekt auch nicht null

30:23.320 --> 30:25.540
ist, damit ich auf die Attribute auch gut zugreifen kann.

30:25.540 --> 30:29.180
Dann prüfe, ob die beiden auch zur gleichen Klasse gehören, das hatten

30:29.180 --> 30:31.540
wir eben bei dem Integer-Float-Beispiel gesehen, dass das so eine

30:31.540 --> 30:35.420
typische Herangehensweise bei Equals ist.

30:36.060 --> 30:39.680
Wenn das auch der Fall ist, dann konvertiere ich erstmal meinen

30:39.680 --> 30:43.760
übergebenen Parameter zu einem Golfball, denn der Parameter hat hier

30:43.760 --> 30:46.940
den Typ Object noch sehr allgemein, und kann dann hineingehen und

30:46.940 --> 30:50.360
tatsächlich die Attribute nach und nach vergleichen.

30:50.360 --> 30:54.300
Also ich sage, die zwei Golfbälle sind dann gleich, wenn der Brand

30:54.300 --> 30:57.580
gleich dem anderen Brand ist, Make auch gleich und Compression auch

30:57.580 --> 30:57.820
gleich.

30:57.900 --> 31:00.080
Wenn das alles wahr ist, gebe ich true zurück.

31:00.600 --> 31:05.520
Hier sind Brand und Make zwei Strings, das sind auch Objekte, und ich

31:05.520 --> 31:08.820
kann darauf die Equals-Methode wieder aufrufen, die beim String auch

31:08.820 --> 31:10.240
so implementiert ist, wie ich es erwarten würde.

31:10.380 --> 31:13.180
Es werden halt die Zeichenketten auf inhaltliche Gleichheit geprüft.

31:13.600 --> 31:16.180
Und Compression, wenn ich es richtig weiß, ist sogar Integer oder

31:16.180 --> 31:18.780
sowas, kann ich mit gleich gleich vergleichen.

31:19.300 --> 31:22.280
Genau, wenn die nicht gleich sind, dann gebe ich false zurück.

31:22.560 --> 31:25.780
Dann, wenn ich das ausführe, also wenn ich dann meinen Testcode von

31:25.780 --> 31:29.200
eben ausführe und diese Methode hinzugefügt habe, bekomme ich endlich

31:29.200 --> 31:32.080
das gewünschte Ball1 equals Ball2.

31:35.700 --> 31:40.760
Alles funktioniert mit Attributen vom Typ String, wie erwartet.

31:40.760 --> 31:43.340
Ich habe jetzt hier auf den Folien nochmal ein bisschen weiteres

31:43.340 --> 31:46.660
Detail, wenn man jetzt nämlich wieder guckt, was wäre denn, wenn wir

31:46.660 --> 31:51.500
hier Attribute nicht vom Typ String benutzen, sondern vom Typ String

31:51.500 --> 31:55.780
-Buffer, wäre dann wieder ein Beispiel, wo es ein bisschen

31:55.780 --> 31:56.660
unerwarteter ist.

31:56.740 --> 31:59.720
Aber ich glaube, die Methoden, also diese Punkte würde ich jetzt doch

31:59.720 --> 32:04.140
mal, wo heute ein bisschen viel haben, überspringen.

32:04.140 --> 32:06.460
Aber da kann es vielleicht einfach nur so als Hinweis, wenn man jetzt

32:06.460 --> 32:09.180
wieder andere Attribute verwenden würde, oder andere Klassen hier

32:09.180 --> 32:12.360
verwenden würde, muss man immer gucken, ob eine Klasse, für die ich

32:12.360 --> 32:16.900
equals aufrufen möchte, auch tatsächlich equals so implementiert, wie

32:16.900 --> 32:18.160
ich das brauche.

32:18.260 --> 32:22.720
Beim String-Buffer ist es eben gerade wieder nicht der Fall.

32:22.820 --> 32:27.400
Da würde es wieder das not equal herauskommen.

32:27.900 --> 32:31.460
Da könnte ich dann hergehen und sagen, okay, dann implementiere ich

32:31.460 --> 32:34.340
mir meine eigene String-Buffer-Klasse.

32:35.340 --> 32:37.000
Aber wie gesagt, da will ich jetzt gar nicht so weit ins Detail

32:37.000 --> 32:41.100
reingehen, dass ich glaube, den Punkt, dass Sie equals selber

32:41.100 --> 32:43.980
implementieren müssen, und dass Sie dann, das ist eben hier der

32:43.980 --> 32:47.780
weitere Punkt, auch bei der Verwendung von equals in Ihrer equals

32:47.780 --> 32:50.120
-Methode auch wieder aufpassen müssen, was passiert hier eigentlich

32:50.120 --> 32:54.980
und ist das Verständnis von equals, was Sie an der Stelle brauchen,

32:55.100 --> 32:58.400
auch das, was der Implementierer der Methode implementiert hat.

32:59.060 --> 33:01.000
Genau, darauf müssen Sie eben achten.

33:01.460 --> 33:03.900
Ich bringe jetzt ansonsten mal ein bisschen weiter.

33:08.220 --> 33:11.460
Genau, das gehört alles noch zu dieser String-Buffer-Beispiel, aber

33:11.460 --> 33:15.160
das, wie gesagt, können Sie sich gerne nochmal in Ruhe anschauen.

33:15.160 --> 33:17.700
Steht, glaube ich, auch alles Notwendige auf den Folien drauf, aber

33:17.700 --> 33:21.820
die Botschaft ist einfach, bei equals aufpassen, was in der Definition

33:21.820 --> 33:24.820
der Methoden, die Sie da verwenden oder bei vorgefertigten Klassen

33:24.820 --> 33:25.820
eben tatsächlich drin steht.

33:29.080 --> 33:32.720
Genau, insgesamt gibt es ein paar Punkte, wo man, das ist jetzt das

33:32.720 --> 33:37.640
Prinzip 4, wo man bei der Implementierung von equals aufpassen sollte,

33:37.640 --> 33:40.920
oder anders gesagt, auf die Nase fallen könnte.

33:41.740 --> 33:48.560
Heißt, wenn man equals selbst implementiert, sollte man sich die

33:48.560 --> 33:50.020
folgenden Fragen erstmal stellen.

33:50.560 --> 33:54.340
Man sollte sich fragen, braucht meine Klasse überhaupt eine equals

33:54.340 --> 33:54.620
-Methode?

33:54.800 --> 33:56.720
Also es ist jetzt nicht so, dass Sie für jede Klasse, die Sie

33:56.720 --> 33:58.980
schreiben, immer die Methode equals überschreiben müssen.

33:59.580 --> 34:01.900
Das macht nicht unbedingt Sinn, wenn Sie in Ihrem Programm diesen

34:01.900 --> 34:05.080
Vergleich auf inhaltliche Gleichheit gar nicht brauchen.

34:05.080 --> 34:08.680
Sie können sich das also sparen für die Fälle, wo Sie wirklich Objekte

34:08.680 --> 34:11.280
der Klassen auch logisch miteinander vergleichen wollen.

34:11.420 --> 34:14.720
Wo der Vergleich von zwei Referenzen nicht ausreicht.

34:17.840 --> 34:21.060
Genau, was wir eben gesehen haben, der equals-Methode, kann ich nicht

34:21.060 --> 34:23.440
unbedingt so auf alle anderen Implementierungen machen.

34:23.580 --> 34:26.480
Also insbesondere, wenn ich dann Vererbungshierarchien habe, wie ich

34:26.480 --> 34:29.660
gleich sehen werde, muss man noch ein bisschen genauer gucken, was ich

34:29.660 --> 34:31.080
denn da reinschreiben muss.

34:31.080 --> 34:33.680
Eigentlich bei der equals-Methode, wo ich dann auch wieder intern

34:33.680 --> 34:35.920
diesen Gleich-Gleich-Vergleich benutzen kann für Attribute.

34:36.460 --> 34:39.840
Wo ich mir für meine Attribute vielleicht eigene Vergleiche noch

34:39.840 --> 34:40.460
schreiben muss.

34:40.920 --> 34:42.860
Das wäre dieses StringBuffer-Beispiel gerade gewesen.

34:43.240 --> 34:47.420
Und wo ich die equals-Methode von den Typen, die meine Attribute

34:47.420 --> 34:49.980
haben, aufrufen kann.

34:51.040 --> 34:54.300
Also da genau nachvollziehen, was eigentlich passiert.

34:55.020 --> 34:58.700
Und dann kann ich mich auch noch fragen, welche Objekte sollen denn

34:58.700 --> 34:59.660
eigentlich verglichen werden?

34:59.660 --> 35:02.960
Also wie gesagt, der Standardfall oder typische Fall in Java ist, dass

35:02.960 --> 35:08.480
nur Objekte derselben Klasse dann auch zu equals-true kommen können.

35:08.880 --> 35:12.280
Aber es gibt auch manchmal Fälle, wo man auch Objekte von Unterklassen

35:12.280 --> 35:15.300
mit Objekten der Basisklasse vergleichen müsste.

35:15.580 --> 35:20.180
Beispiel wäre, dass ich vielleicht Klassen, Personen, Employee,

35:20.720 --> 35:21.440
Student habe.

35:21.640 --> 35:24.180
Und da würde ich vielleicht schon mal vergleichen wollen, ob ein

35:24.180 --> 35:26.920
Employee gleich einem Student ist, also einem Studenten.

35:26.920 --> 35:29.680
Es könnte ja eigentlich in der realen Welt die gleiche

35:29.680 --> 35:30.860
dahinterliegende Person sein.

35:31.500 --> 35:34.020
Und das würde ich vielleicht in der Implementierung einer equals

35:34.020 --> 35:36.260
-Methode auch widerspiegeln wollen.

35:36.420 --> 35:40.900
Also das, was equal ist, hängt eben auch von dem Zweck Ihres Programms

35:40.900 --> 35:41.700
ab.

35:41.940 --> 35:43.960
Muss sich dann aber auch in der Implementierung widerspiegeln, das

35:43.960 --> 35:46.480
kann das Programm natürlich nicht von alleine raten, was Sie gerne

35:46.480 --> 35:46.740
hätten.

35:48.500 --> 35:51.400
Okay, kommen wir noch zu einem weiteren Prinzip.

35:51.400 --> 35:54.700
Tupper.equals verwenden.

35:55.360 --> 35:59.060
Das bezieht sich eben gerade darauf, wie ich diese equals-Methode in

35:59.060 --> 36:02.300
Vererbungshierarchien richtig benutze.

36:03.140 --> 36:06.560
Da stellt sich die Frage, was passiert denn mit dieser equals-Methode

36:06.560 --> 36:07.380
bei der Vererbung?

36:08.080 --> 36:10.900
Und insbesondere, was ich gerade schon ein bisschen angesprochen habe,

36:11.240 --> 36:13.860
kann ich Objekte der abgeleiteten Klassen eigentlich mit Objekten der

36:13.860 --> 36:15.860
Basisklasse vergleichen.

36:17.640 --> 36:21.920
Schauen wir uns hier auch erstmal ein Beispiel für fehlerhaften Code

36:21.920 --> 36:22.260
an.

36:22.500 --> 36:25.980
Also jetzt nochmal eine Erweiterung von unserer Golfball-Klasse.

36:27.340 --> 36:34.840
Wo ich bei Konstanten habe, die sagt, ein Two-Piece-Golfball oder ein

36:34.840 --> 36:36.180
Three -Piece-Golfball.

36:36.960 --> 36:42.340
Ich habe einen Konstruktor, der eben meine Attribute setzt und das

36:42.340 --> 36:43.000
auch schön macht.

36:43.000 --> 36:46.700
Also die Attribute der Oberklasse Golfball setzt, indem ich den

36:46.700 --> 36:52.260
Konstruktor der Oberklasse aufrufe und dann nur dieses neue Feld

36:52.260 --> 36:54.280
Construction setze.

36:54.480 --> 36:57.360
Construction kann eben zwei Werte haben, entweder es ist ein Two-Piece

36:57.360 --> 37:02.700
oder ein Three-Piece-Golfball.

37:03.340 --> 37:05.200
Und hat eben diesen weiteren Parameter.

37:05.940 --> 37:08.560
Ich habe also auch für dieses neue Attribut Construction, was ich

37:08.560 --> 37:12.120
hinzugefügt habe, auch eine Methode, die mir das zurückliefert.

37:12.520 --> 37:13.200
All Construction.

37:13.860 --> 37:17.520
Und würde jetzt also eine Equals-Methode auch für meine neue Golfball

37:17.520 --> 37:20.200
-Klasse implementieren wollen.

37:20.500 --> 37:23.100
Könnte ich jetzt erstmal sagen, okay, ich mache hier wieder diesen

37:23.100 --> 37:24.920
Vergleich, ob die Referenzen gleich sind.

37:26.260 --> 37:30.700
Das lasse ich so, dann prüfe ich auch nochmal, ob die Objekte, also

37:30.700 --> 37:33.160
einerseits, ob die gegebene Referenz nicht null ist und ob die Objekte

37:33.160 --> 37:34.160
zur gleichen Klasse gehören.

37:34.160 --> 37:40.340
Wenn ja, caste ich mein Objekt auch zu einem Golfball, vergleiche dann

37:40.340 --> 37:45.020
noch, ob die Attribute hier in meiner Klasse gleich sind.

37:45.440 --> 37:48.940
Wenn ja, return true, wenn nicht, return false.

37:50.740 --> 37:53.340
Wäre jetzt, wenn ich nach diesem stumpfen Schema vorgehe, dass ich

37:53.340 --> 37:56.660
sage, ich vergleiche alle Attribute meiner Klasse, ob die gleich sind

37:56.660 --> 37:59.420
und wenn ja, ist mein Golfball gleich.

37:59.420 --> 38:04.360
Das ist natürlich hier erstmal nicht passend und richtig, bei so einer

38:04.360 --> 38:06.940
Implementierung, ist Ihnen vielleicht jetzt auch aufgefallen, sind

38:06.940 --> 38:10.020
zwei Golfbälle bereits gleich, wenn nur dieses Ball-Construction

38:10.020 --> 38:11.880
-Attribut denselben Wert hat.

38:11.920 --> 38:13.700
Und das macht eben von der Bedeutung hier keinen Sinn.

38:13.800 --> 38:17.560
Wir haben ja gerade gesagt, beim Golfball allgemein ist eben auch die

38:17.560 --> 38:21.380
Marke und das Modell von dem Golfball relevant.

38:21.780 --> 38:24.880
So funktioniert das hier also nicht so gut.

38:26.040 --> 38:29.140
Wir können uns das nochmal im Detail anschauen, was würde passieren,

38:29.540 --> 38:33.060
wenn ich jetzt wieder meine Warehouse-Komponent habe, also diese

38:33.060 --> 38:36.380
Testklasse sozusagen, die ich eben schon eingeführt habe, zwei solche

38:36.380 --> 38:37.700
MyGolfBall anlege.

38:39.040 --> 38:41.040
Erstmal mit gleichen Attributen.

38:44.260 --> 38:46.200
Dann ist es ja wunderbar.

38:46.620 --> 38:50.000
Was passiert aber, wenn ich bei diesen zwei Golfball-Attributen

38:50.000 --> 38:54.260
unterschiedliche Objekte reingebe, also hier zum Beispiel diesen Wert,

38:54.560 --> 38:55.200
was war es nochmal?

39:03.620 --> 39:05.420
Wie sehen, ich spiele kein Golf, wie gesagt.

39:05.560 --> 39:06.480
Ah ja, Compression war es.

39:07.800 --> 39:09.020
Ist ja auch egal an der Stelle.

39:09.380 --> 39:12.000
Also diesen Wert Compression hier unterschiedlich initialisiere, also

39:12.000 --> 39:15.440
hier tatsächlich zwei verschiedene Arten von Bällen habe.

39:15.440 --> 39:19.380
Wenn ich jetzt damit die Equals-Methode aufrufen würde, würde ich

39:19.380 --> 39:22.640
rausbekommen, dass diese beiden Bälle gleich sind, weil die

39:22.640 --> 39:24.740
Implementierung der Equals-Methode, die ich jetzt gerade für

39:24.740 --> 39:30.660
MyGolfBall gemacht hatte, eben nur das letzte Attribut Construction

39:30.660 --> 39:31.600
verglichen hat.

39:33.160 --> 39:34.720
Warum verhält sich das Programm so, habe ich jetzt schon

39:34.720 --> 39:35.240
vorweggenommen.

39:36.700 --> 39:40.320
Dieser Code in der Main-Methode ruft ja immer die speziellste Equals

39:40.320 --> 39:43.480
-Methode auf, dynamische Bindung, oder sowieso an der Stelle, war es

39:43.480 --> 39:45.800
ja glaube ich auch noch so deklariert, wird immer die speziellste

39:45.800 --> 39:49.200
Equals -Methode aufgerufen und in der wurde eben nur Ball Construction

39:49.200 --> 39:50.160
verglichen.

39:50.360 --> 39:53.360
Und der Rest, die Equals-Methode von Golfball, wird erstmal gar nicht

39:53.360 --> 39:54.640
ausgeführt, die habe ich ja überschrieben.

39:55.260 --> 39:58.700
Das heißt, die Attribute mit Brand, Make, Compression werden nicht

39:58.700 --> 39:59.480
verglichen.

40:00.260 --> 40:01.100
Wie kann ich das lösen?

40:01.780 --> 40:07.420
Ja, kann ich recht einfach lösen.

40:07.500 --> 40:09.680
Ich mache am besten trotzdem zuerst diesen Vergleich über die

40:09.680 --> 40:12.800
Referenzen und füge in meine Klasse eben noch an geeigneter Stelle

40:12.800 --> 40:15.960
oder in meine Methode an geeigneter Stelle noch den Aufruf super

40:15.960 --> 40:17.620
.equals ein.

40:17.620 --> 40:20.060
Also es ist jetzt auch nicht die richtige Lösung, an dieser Stelle

40:20.060 --> 40:23.020
jetzt den Vergleich der Attribute von Hand zu machen, deswegen steht

40:23.020 --> 40:25.700
das hier gar nicht erst, sondern was ich natürlich tun sollte, ist die

40:25.700 --> 40:28.660
Implementierung, die ja schon vorliegt, in meiner Oberklasse einfach

40:28.660 --> 40:33.580
explizit aufzurufen, indem ich auch super.equals aufrufe und so keine

40:33.580 --> 40:36.600
Code -Uplizierung habe, gleichzeitig aber diesen semantischen

40:36.600 --> 40:39.860
Vergleich, der in der Oberklasse realisiert ist, hier unten dann auch

40:39.860 --> 40:41.080
nutzen kann, sozusagen.

40:43.520 --> 40:45.920
Das wäre eine etwas längere Lösung für dieses Problem.

40:46.060 --> 40:49.020
Es gibt auch eine etwas kürzere Lösung.

40:50.440 --> 40:53.380
Wenn ich nämlich schon weiß, dass ja in meiner Oberklasse dieser

40:53.380 --> 40:56.180
Vergleich mit der Referenz schon gemacht wird und der Vergleich, ob es

40:56.180 --> 40:59.160
die gleiche Klasse ist, kann ich mir das hier auch sparen und direkt

40:59.160 --> 41:03.200
nur schreiben, wenn super.equals ist, das heißt, wenn mein Objekt

41:03.200 --> 41:06.860
schon auf alle Attribute bis auf meine neun gleich ist, dann

41:06.860 --> 41:09.120
vergleiche ich jetzt nochmal neues Attribut und wenn das dann auch

41:09.120 --> 41:11.060
gilt, dann sind die beiden Golf-Bänder gleich.

41:11.080 --> 41:14.340
Ist ein bisschen kürzer, publiziert nicht.

41:15.000 --> 41:17.900
Müssen Sie aber ein bisschen aufpassen, weil hier natürlich die

41:17.900 --> 41:21.300
Annahme ist oder Sie wissen müssen, wie die Equals-Methode in der

41:21.300 --> 41:23.200
Basisklasse implementiert ist.

41:23.460 --> 41:25.460
Andererseits müssen Sie das irgendwo auch immer wissen.

41:26.000 --> 41:29.080
Von daher kann man diese Lösung schon an vielen Stellen verwenden.

41:29.720 --> 41:35.460
Okay, was passiert, wenn ich mehrere Ebenen in meiner Vererbungs

41:35.460 --> 41:36.120
-Hierarchie habe?

41:36.200 --> 41:38.540
Wir haben ja unser Memmel-Beispiel gehabt mit ganz vielen Klassen, die

41:38.540 --> 41:39.380
voneinander erben.

41:40.620 --> 41:45.140
Ja, das ist eigentlich kein weiteres großes Problem, weil ich eben

41:45.140 --> 41:52.600
diese Klasse myGolfBall jetzt super.equals aufruft, funktioniert alles

41:52.600 --> 41:52.900
gut.

41:52.900 --> 41:56.660
Wenn ich noch eine Klasse dazwischen einfüge, die auch wieder super

41:56.660 --> 42:00.240
.equals aufruft, dann funktioniert mein Vergleich trotzdem wie

42:00.240 --> 42:00.620
erwartet.

42:00.740 --> 42:03.160
Oder wenn eine Klasse dazwischen eingefügt wird in der Vererbungs

42:03.160 --> 42:06.060
-Hierarchie, die neue Attribute auch hinzufügt, werden ja trotzdem die

42:06.060 --> 42:09.500
Equals -Methoden aller Klassen, wenn ich immer super.equals

42:09.500 --> 42:11.340
reinschreibe, auch aufgerufen.

42:12.820 --> 42:17.180
Genau, eine Daumenregel hier ist, dass sobald eine Basis-Klasse die

42:17.180 --> 42:22.020
Equals -Methode implementiert, sollten auch die Equals-Methoden der

42:22.020 --> 42:26.020
abgeleiteten Klassen diese auch aufrufen, indem sie super.equals

42:26.020 --> 42:26.360
nutzen.

42:26.560 --> 42:29.340
Weil sonst ist die Wahrscheinlichkeit groß, dass sie irgendwie einen

42:29.340 --> 42:33.260
Wert oder irgendwie einen Aspekt des Vergleiches entweder vergessen

42:33.260 --> 42:36.360
oder aber den Code dafür duplizieren und beides ist schlecht.

42:38.620 --> 42:44.660
Ausnahme wäre Basis-Klasse, also für Objects brauchen sie keinen super

42:44.660 --> 42:47.140
Punkt irgendwas aufzurufen, weil da ja sowieso kein semantischer

42:47.140 --> 42:48.740
Vergleich stattfindet.

42:50.580 --> 42:54.120
Okay, noch ein letzter Punkt, glaube ich, zur Verwendung von

42:54.120 --> 42:58.120
InstanceOf in, also zu Equals, insbesondere die Verwendung von

42:58.120 --> 43:00.840
InstanceOf in Equals.

43:02.520 --> 43:07.820
Bisher hatten wir immer nur Objekte von Klassen in derselben

43:07.820 --> 43:10.760
Vererbungsebene miteinander verglichen und hatten eben nochmal

43:10.760 --> 43:15.200
abgeprüft mit class, ist das auch wirklich die Instanz der gleichen

43:15.200 --> 43:17.680
Klasse und nur dann haben wir gesagt, dass es gleich ist.

43:17.720 --> 43:19.720
Hatten wir bei Integer und Float gerade gesehen, wenn die beiden

43:19.720 --> 43:22.660
Zahlen sind eigentlich irgendwie semantisch gleich, aber nicht Instanz

43:22.660 --> 43:26.040
der gleichen Klasse, also sagen wir, es ist False.

43:26.840 --> 43:29.760
Das will man aber vielleicht nicht immer so haben, das wäre jetzt

43:29.760 --> 43:34.980
gerade dieses Beispiel mit Student, Employee und Person gewesen.

43:35.920 --> 43:41.020
Und wenn man das nicht haben will, was können wir dann tun?

43:41.140 --> 43:43.260
Es gibt noch den anderen Fall, wo das wahrscheinlich sogar recht

43:43.260 --> 43:46.860
sinnvoll ist, wenn nämlich eine abgeleitete Klasse nur das Verhalten

43:46.860 --> 43:50.760
der Basis-Klasse ändert und gar keine neuen Attribute hinzufügt.

43:50.760 --> 43:53.460
Dann würde ich ja eigentlich schon sagen wollen, naja die Attribute

43:53.460 --> 43:56.980
sind gleich und die beiden Objekte sollen gleich sein, im Sinne von,

43:57.080 --> 44:00.140
dass sie den gleichen Zustand haben zum Beispiel, aber das würde mit

44:00.140 --> 44:02.540
dieser Implementierung von Equals, die wir gerade gesehen haben, so

44:02.540 --> 44:04.720
erstmal nicht funktionieren.

44:05.180 --> 44:08.520
Da stellt sich die Frage, wie könnte ich denn dann Objekte dieser

44:08.520 --> 44:13.020
abgeleiteten Klassen mit den Objekten der Basis-Klasse vergleichen?

44:14.860 --> 44:18.200
Ich kann da eben gucken, dass ich in der Implementierung dieser

44:18.200 --> 44:22.960
Methode nicht mit GetClass überprüfe, ob die beiden Objekte wirklich

44:22.960 --> 44:27.580
Instanzen der genau selben Klasse sind, sondern InstanceOf verwenden,

44:27.700 --> 44:29.860
um zu gucken, ob sie nur kompatibel zueinander sind.

44:29.960 --> 44:33.620
Also InstanceOf heißt ja, InstanceOfMammal, ein Dog ist auch ein

44:33.620 --> 44:35.640
InstanceOfMammal zum Beispiel.

44:36.460 --> 44:39.740
Also was ist Cast-kompatibel, sag ich jetzt mal,

44:39.740 --> 44:40.920
Konvertierungskompatibel.

44:41.860 --> 44:44.560
Wenn ich GetClass verwende, sind die Objekte der abgeleiteten Klassen

44:44.560 --> 44:47.080
eben niemals gleich mit den Objekten der Basis-Klasse, weil der Typ

44:47.080 --> 44:48.120
noch mit einbezogen wird.

44:48.960 --> 44:53.500
Wenn ich InstanceOf verwende, können auch abgeleitete Objekte gleich

44:53.500 --> 44:53.800
sein.

44:55.880 --> 45:00.500
Sowas sollte ich aber nur tun, wenn die abgeleitete Klasse selber die

45:00.500 --> 45:03.520
Equals -Methode nicht implementiert, weil das würde keinen Sinn

45:03.520 --> 45:05.200
machen, wenn die wieder was Widersprüchliches tut.

45:05.200 --> 45:08.560
Und wenn die abgeleitete Klasse eben auch keine weiteren Attribute hat

45:08.560 --> 45:11.880
oder zumindest keine weiteren Attribute für die Gleichheit zu

45:11.880 --> 45:13.300
berücksichtigen sind.

45:14.720 --> 45:17.540
Was wäre so ein Beispiel für einen Fall, wo man das schon ganz gut

45:17.540 --> 45:18.140
machen könnte?

45:18.620 --> 45:21.780
Nehmen wir mal an, wir haben eine Klasse Car mit zwei Attributen.

45:22.080 --> 45:25.020
Wieder, was ist das für ein Auto und wann wurde es gebaut?

45:25.640 --> 45:28.200
Dieses Auto kann fahren, da würde irgendwelcher Code drinstehen und

45:28.200 --> 45:32.060
hat eben eine Equals-Methode, die die beiden Attribute auf Gleichheit

45:32.060 --> 45:32.280
überprüft.

45:32.280 --> 45:36.080
Und dann könnte ich noch eine Erweiterung davon haben, MyCar, die

45:36.080 --> 45:41.780
irgendwelche Funktionalität für das Verhalten der Klasse nur

45:41.780 --> 45:44.140
hinzufügt, aber keine neuen Attribute.

45:45.780 --> 45:49.820
Hier nochmal das allgemein formuliert, ich habe eine Klasse Base, ich

45:49.820 --> 45:53.180
habe eine Klasse Derived, Extents Base und Derived fügt keine

45:53.180 --> 45:54.100
Attribute hinzu.

45:54.100 --> 46:01.300
Wenn ich jetzt einfach nur die Equals-Methode mit diesem Vergleich von

46:01.300 --> 46:06.980
GetClass hätte, würde ich eben hier bei einem Equals-Vergleich eines

46:06.980 --> 46:09.820
Basisobjekts mit einem Derived-Objekt Falls bekommen.

46:10.200 --> 46:13.320
Wenn ich aber hier in der Equals-Methode mit InstanceOf vergleiche für

46:13.320 --> 46:17.320
die Frage, sind die gleich, dann würde an so einer Stelle tatsächlich

46:17.320 --> 46:21.020
auch ein True für das Equals herauskommen.

46:24.230 --> 46:27.090
Genau, auch wenn ich es hier andersrum schreiben würde, also ob ich

46:27.090 --> 46:32.390
jetzt schreibe d.equals b oder d.equals d, würde trotzdem True

46:32.390 --> 46:36.050
ergeben, also egal welches von den beiden Objekten ich nach vorne

46:36.050 --> 46:36.650
stelle.

46:37.010 --> 46:39.870
Und das war, wenn Sie sich an die Spezifikation von Equals erinnern,

46:39.890 --> 46:43.390
ja auch wichtig, bis die Symmetrie-Eigenschaft gegeben ist.

46:45.590 --> 46:47.070
Genau, was könnte ich jetzt noch machen?

46:47.190 --> 46:53.470
Ah ja, genau, man muss bei der Implementierung von Equals aufpassen.

46:53.950 --> 46:56.730
Hier würden wir jetzt bei der Derived-Methode selber auch ein Equals

46:56.730 --> 46:58.150
hinzufügen.

47:01.070 --> 47:05.870
Genau, wenn wir das jetzt so machen, dass wir hier ein Equals

47:05.870 --> 47:12.970
hinzufügen, was super.equals mit dem gleichen Objekt aufruft, dann

47:12.970 --> 47:16.170
würde dieser Aufruf hier immer True ergeben.

47:16.490 --> 47:18.270
Die Symmetrie würde verloren gehen.

47:18.370 --> 47:19.010
Warum ist das so?

47:20.010 --> 47:25.470
Hier würde ich in der Equals-Methode der abgeleiteten Klasse mit einem

47:25.470 --> 47:28.470
Objekt vom Typ Base das aufrufen.

47:28.890 --> 47:32.070
Wegen dieser super.equals würde der Code in der Basis-Klasse

47:32.070 --> 47:39.310
aufgeführt werden und dieser vergleichte Objekt Instance of Base wäre

47:39.310 --> 47:41.350
eben immer gültig.

47:41.350 --> 47:43.570
Das muss aber nicht unbedingt immer stimmen.

47:43.670 --> 47:47.990
Alle Objekte der Unterklasse müssen auch tatsächlich gleich sein, wenn

47:47.990 --> 47:50.890
ich hier doch weitere vergleiche, weitere Attribute vielleicht habe.

47:54.410 --> 47:58.710
Okay, ich kann prüfen, ob das gegebene Objekt Instance of Derived ist,

47:58.950 --> 48:04.110
aber dann hätte ich das Problem, dass ich hier in der Kombination ein

48:04.110 --> 48:09.130
False hätte, wenn ich die zwei Objekte miteinander vergleiche.

48:09.130 --> 48:14.790
Wenn ich dann das umgekehrt aufrufe, ich kann jetzt leider in einer

48:14.790 --> 48:18.270
Minute das nicht mehr ganz im Detail verwendern, hätte ich allerdings

48:18.270 --> 48:19.150
ein True-Effekt.

48:19.330 --> 48:22.930
Also das ist ein Problem, wenn ich Equals-Methoden so implementiere,

48:23.010 --> 48:26.870
wie ich es jetzt hier gerade habe, würde die Symmetrie-Eigenschaft

48:26.870 --> 48:27.590
verloren gehen.

48:27.730 --> 48:33.150
Wenn ich das d.equals b formuliere, würde ich immer False bekommen und

48:33.150 --> 48:36.190
b.equals d würde ich dann eben True bekommen, wenn es tatsächlich auch

48:36.190 --> 48:37.170
passend ist.

48:37.170 --> 48:39.230
Da muss ich also auch hier aufpassen.

48:39.750 --> 48:42.850
Also, ja, das ist ein Verhalten, was sehr unintuitiv ist, wenn die

48:42.850 --> 48:45.730
Symmetrie -Eigenschaft b.equals nicht mehr gegeben ist.

48:45.950 --> 48:49.390
Da muss ich genau wissen, welche Klasse hier, oder welches Objekt, wo

48:49.390 --> 48:50.430
welche Klasse dahinter steckt.

48:50.490 --> 48:54.870
Das sollte man also versuchen zu vermeiden, um es kurz zu machen.

48:55.970 --> 48:56.350
Okay.

48:56.670 --> 48:58.930
Zum Glück sind wir zeitlich jetzt hier auch schon bei der

48:58.930 --> 49:00.450
Zusammenfassung angekommen.

49:04.930 --> 49:08.710
Kurz gesagt, muss man b.equals darauf aufpassen, dass die

49:08.710 --> 49:10.750
Implementierung auch symmetrisch ist.

49:10.850 --> 49:13.910
Also, wenn ich anfange, über die Vererbungs-Hierarchie Elemente zu

49:13.910 --> 49:16.970
vergleichen, muss ich wirklich sehr aufpassen.

49:17.070 --> 49:19.010
Das wäre jetzt die kurze Variante.

49:19.010 --> 49:21.310
Ich habe leider, fällt mir gerade auf, noch ein paar mehr Folien.

49:21.430 --> 49:23.530
Nämlich einmal einen Hinweis, dass das natürlich jetzt noch nicht

49:23.530 --> 49:25.650
alles war, was es zum Thema Java zu sagen gibt.

49:26.150 --> 49:28.170
Insbesondere haben wir noch keine Lebensläufigkeit angeschaut.

49:28.250 --> 49:30.430
Wir haben uns noch nicht Security, wir haben uns noch nicht

49:30.430 --> 49:33.450
Komponenten angeschaut, keine Entwurfsmuster, keine grafischen

49:33.450 --> 49:33.970
Oberflächen.

49:34.090 --> 49:36.270
Also, Sie sind jetzt noch nicht direkt Java-Experten, wenn Sie hier

49:36.270 --> 49:36.750
rausgehen.

49:37.030 --> 49:38.350
Das nur als Hinweis.

49:38.630 --> 49:40.490
Kommt aber in späteren Vorlesungen auch noch.

49:41.210 --> 49:42.630
Und, genau.

49:44.330 --> 49:47.010
Für diese Werbefolie habe ich jetzt wahrscheinlich nicht mehr die

49:47.010 --> 49:50.990
Zeit, aber wenn Sie sich ganz kurz ein Pro-Seminar interessieren.

49:51.190 --> 49:54.130
Wir hatten ja einige Software-Katastrophen hier im Programmieren schon

49:54.130 --> 49:55.070
angesprochen.

49:55.450 --> 49:58.250
Da veranstalten wir auch ein Pro-Seminar dafür.

49:58.830 --> 50:00.250
Termine habe ich schon genannt.

50:01.170 --> 50:02.110
Wie geht es weiter?

50:02.590 --> 50:04.250
Können Sie hier nochmal nachlesen.

50:04.250 --> 50:07.830
Und was ich natürlich nicht vergessen sollte zu sagen ist, wünsche

50:07.830 --> 50:11.750
Ihnen, weil es ja heute die letzte Vorlesung ist, Sie uns nur noch

50:11.750 --> 50:14.110
virtuell im Forum und so weiter sehen.

50:14.370 --> 50:19.610
Wünsche ich euch an dieser Stelle hier viel Erfolg im weiteren Studium

50:19.610 --> 50:22.390
und hoffe, dass man sich noch in der ein oder anderen Software-Technik

50:22.390 --> 50:24.190
-Vorlesung über den Weg läuft.

50:24.730 --> 50:28.270
Also alles Gute, viel Spaß bei den Abschlussaufgaben und bis dann.

