GOTO (aus Digitaluhr, Hilfe !)

Diskutiere GOTO (aus Digitaluhr, Hilfe !) im Java Basics - Anfänger-Themen Bereich.
Blender3D

Blender3D

Diese ganzen ifs machen mich ganz wuschig. :) Mit Hilfe von Java 9 VarHandles und Lambdas kann man das noch etwas abstrahieren. Naja... nur, dass man hierfür Instanzvariablen benötigt... aber egal:
Nette Variante mit Nachteilen:
a) Unübersichtlicher Code.
b) Abhängigkeit von Java 9

Die vielen Ifs kann man auch mittels einer Schleife kaschieren.
Java:
public class TimeTicker {
    private int[] max = { 10, 60, 60, 24 };
    private int[] time = { 0, 0, 0, 0 };
    public final static int PART_OF_SECOND = 0;
    public final static int SECOND = 1;
    public final static int MINUTE = 2;
    public final static int HOUR = 3;

    private int countUpValue(int value, int max) {
        value++;
        return (value >= max) ? 0 : value;
    }

    public long getExpectedDelayInMilliSeconds() {
        return 1000l / max[PART_OF_SECOND];
    }

    public void tick() {
        for (int i = 0; i < time.length; i++) {
            time[i] = countUpValue(time[i], max[i]);
            if (time[i] != 0)
                break;
        }
    }

    @Override
    public String toString() {
        return String.format("%d:%02d:%02d,%d", time[HOUR], time[MINUTE], time[SECOND], time[PART_OF_SECOND]);
    }

}
Java:
public class start {
    public static void main(String[] args) {
        TimeTicker time = new TimeTicker();
        while (true) {
            time.tick();
            try {
                Thread.sleep(time.getExpectedDelayInMilliSeconds());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(time);
        }
    }
}
 
T

Tobias-nrw

@Blender3D Etwas unschön:
1. break
2. magische Konstanten
3. nicht an der aktuellen Zeit angelegt
4. feste Wartezeit von 100ms, dadurch ist der TimeTicker ungenau
5. final vergessen
6. final an unüblicher Stelle
7. Logik in der main
8. zu klein gewählter try-catch
9. nach 24 Stunden fliegt Dir alles um die Ohren :oops:

Java:
public class Uhr extends Thread {
    private final int[] max = { 10, 60, 60, 24 };
    private final int[] time = { 0, 0, 0, 0 };
    private static final int expectedDelayInMilliSeconds = 100;
    public static final int PART_OF_SECOND = 0;
    public static final int SECOND = 1;
    public static final int MINUTE = 2;
    public static final int HOUR = 3;

    private int countUpValue(int value, int max) {
        value++;
        return (value >= max) ? 0 : value;
    }

    public long getExpectedDelayInMilliSeconds() {
        return expectedDelayInMilliSeconds;
    }

    public void tick() {
        int time;
        int i = 0;
        do {
            time = (this.time[i] = countUpValue(this.time[i], max[i]));
            i++;
        } while (time == 0 && i < max.length);
    }

    @Override
    public String toString() {
        return String.format("%d:%02d:%02d,%d", time[HOUR], time[MINUTE], time[SECOND], time[PART_OF_SECOND]);
    }

    public static void main(String[] args) {
        try {
            Uhr time = new Uhr();
            while (true) {
                time.tick();
                Thread.sleep(time.getExpectedDelayInMilliSeconds());
                System.out.println(time);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
 
Blender3D

Blender3D

1. break
2. magische Konstanten
3. nicht an der aktuellen Zeit angelegt
4. feste Wartezeit von 100ms, dadurch ist der TimeTicker ungenau
5. final vergessen
6. final an unüblicher Stelle
7. Logik in der main
8. zu klein gewählter try-catch
9. nach 24 Stunden fliegt Dir alles um die Ohren :oops:
1.
Java:
public void tick() {
        for (int i = 0; i < time.length; i++) {
            time[i] = countUpValue(time[i], max[i]);
            if (time[i] != 0)
                break;
        }
    }
Durch break wird hier jedes 10 mal eine 2te, jedes 600 mal eine 3te und jedes 36000te mal eine if Bedingung abgefragt
Die Funktion hat die Aufgabe den Zeit Zähler zu erhöhen. Die Abbruchbedingung der Schleife ist hier gut lesbar in dem 4zeiligen Code formuliert. Deine "verbesserte Variante" mit der while Schleife reduziert die Lesbarkeit anstatt sie zu verbessern.
2) Die Konstanten sind nicht magisch sie dienen der Lesbarkeit und können später für eine Getterfunktion der einzelnen Zählerstände benutzt werden. Genauso macht es die Klasse Calendar, die Du in Deiner, nicht der Aufgabenstellung entsprechenden Lösung, verwendest.
3) 4) Das ist nicht die Aufgabe des Timetickers.
5) 6) ein private Array final zu machen hat keinen Effekt in diesem Zusammenhang. Konstant ist da nur die Referenz des auf das Array selbst. Die Konstanten sind natürlich static ,final und public. Sie werden z.B. bei einer zukünftigen getter Funktion von außen benutzt.
7) Die Logik von Timeticker ist nicht in der main. Was Du meinst ist die Testklasse, um den Timeticker zu testen. Wenn es später einmal um Genauigkeit geht wird das von einer Klasse z.B. Uhr gemacht, die diese Aufgabe erfüllt. Deshalb besitzt der TimeTicker auch die Methode
Java:
 public long getExpectedDelayInMilliSeconds() {
        return 1000l / max[PART_OF_SECOND];
    }
Diese gibt keine Konstante zurück, um es zu ermöglichen mit einer setter Funktion die Auflösung des TimeTicker einzustellen.
8) Das Testprogramm macht genau das was es soll.
9) Die Uhr beginnt wieder bei 00:00:00,0
 
T

Tobias-nrw

Durch break wird hier jedes 10 mal eine 2te, jedes 600 mal eine 3te und jedes 36000te mal eine if Bedingung abgefragt
Die Funktion hat die Aufgabe den Zeit Zähler zu erhöhen. Die Abbruchbedingung der Schleife ist hier gut lesbar in dem 4zeiligen Code formuliert. Deine "verbesserte Variante" mit der while Schleife reduziert die Lesbarkeit anstatt sie zu verbessern.
Das sieht Dijkstra anders. ;)

2) Die Konstanten sind nicht magisch sie dienen der Lesbarkeit und können später für eine Getterfunktion der einzelnen Zählerstände benutzt werden.
In der einen Methode hast du eine magische Konstante...

3) 4) Das ist nicht die Aufgabe des Timetickers.
Das stimmt, wenn man die Aufgabe des TEs so auslegen möchte.

5) 6) ein private Array final zu machen hat keinen Effekt in diesem Zusammenhang
Aber in einem Späteren. ;)

Die Logik von Timeticker ist nicht in der main. Was Du meinst ist die Testklasse, um den Timeticker zu testen. Wenn es später einmal um Genauigkeit geht wird das von einer Klasse z.B. Uhr gemacht, die diese Aufgabe erfüllt
Du hast also nur die halbe Aufgabe gelöst. ;)

8) Das Testprogramm macht genau das was es soll
Ja, solange keine Fehler auftreten...

9) Die Uhr beginnt wieder bei 00:00:00,0
Stimmt, das habe ich übersehen. :(

--

Jedenfalls sollte es doch jetzt viiiele Lösungsmöglichkeiten für den TE geben. :)
 
Blender3D

Blender3D

Das sieht Dijkstra anders
Mit welchen Argumenten ? lol Bitte die Quelle mit Kontext angeben, bevor Du solche Dinge in den Raum stellst.

Java:
 public void tick() { // 4 Zeilen Code
        for (int i = 0; i < time.length; i++) { // Bedingung 1 = Länge des Arrays
            time[i] = countUpValue(time[i], max[i]);
            if (time[i] != 0) // Bedingung 2 = Überlauf des jeweiligen Counters
                break;
        }
    }
versus
Java:
public void tick() { // 6 Zeilen Code
        int time; // unnötige Variable
        int i = 0;
        do {
            time = (this.time[i] = countUpValue(this.time[i], max[i]));
            i++;
        } while (time == 0 && i < max.length); // verknüpfte Bedingungen erschwert die Lesbarkeit
    }
In der einen Methode hast du eine magische Konstante...
Falls Du diese Funktion meinst,
Java:
   public long getExpectedDelayInMilliSeconds() {
        return 1000l / max[PART_OF_SECOND];
    }
dann übersetze erst einmal den Methodennamen und Dir wird klar woher die Konstante 1000l kommt. Ob diese Konstante magische ist hängt dann nur mehr von den Fähigkeiten einen Code zu lesen ab. ;)
 
Zuletzt bearbeitet:
T

Tobias-nrw

Ich kann das lesen, allerdings erschwert...

Zum Versus... Das stimmt, eine Variable wäre überflüssig, spart aber einen Array Zugriff und ist leichter zu lesen...

Zu Dijkstra: https://en.wikipedia.org/wiki/Considered_harmful . Genaue Belege habe ich aber nich.

Nimm meine Kritikpunkte doch mal als sehr sinnvoll an. ;)
 
K

kneitzel

Ähm, das break mit einem GOTO zu vergleichen ist gewagt. Ebenso bei aktuellen Clean Code Fragen einen Pionier zu bringen, dessen aktive Zeit gut 2 Jahrzehnte vorbei ist, halte ich ebenso für gewagt.

Daher ist die Frage nach einer Begründung doch durchaus angebracht. Und so Kritik sollte halt begründet werden können. (Und aus aktuellem Anlass: Etwas dann einfach als "Bullshit" zu bezeichnen ist keine Begründung!)
 
Blender3D

Blender3D

Ich kann das lesen, allerdings erschwert...

Zum Versus... Das stimmt, eine Variable wäre überflüssig, spart aber einen Array Zugriff und ist leichter zu lesen...

Zu Dijkstra: https://en.wikipedia.org/wiki/Considered_harmful . Genaue Belege habe ich aber nich.

Nimm meine Kritikpunkte doch mal als sehr sinnvoll an. ;)
Ein Arrayzugriff passiert direkt. (Basics). Wo wird hier irgend etwas gespart? Und leichter zu lesen? --> time = this.time[] . Hier muss ich zwei Variablen betrachten anstatt einer.
Der Link zu Dijkstra ist mir bekannt. Du solltest diesen aber lesen, da geht es um GOTO also einen Sprung. Das hat mit break gar nichts zu tun. Der Unterschied ist Dir aber scheinbar nicht geläufig. Also ich bin gerne bereit begründete Kritikpunkte anzunehmen, weil man sich nur mit Kritikfähigkeit weiterentwickelt. Wobei hier die Betonung auf begründet liegt. Ich kann in Deiner Argumentation keinen wirklich guten Grund für eine Codeänderung finden außer ich möchte diesen 'verschlechtbessern'.
 
K

kneitzel

Gewagt ist auch ein geflügeltes Wort... etwas wischi waschi... Du weißt aber schon, das Dijkstra seit geraumer Zeit nicht mehr unter den Lebenden weilt?
Schön das Thema gewechselt. 99 ist er in Ruhestand gegangen (Also aktive Zeit seid 2 Jahrzehnten vorüber - so wie ich geschrieben habe. Seine großen Veröffentlichungen waren sogar deutlich vor 99!), 2002 gestorben (Was aber in diesem Zusammenhang egal ist. Entscheidend sind ja die Aussagen, die er in seiner aktiven Zeit gemacht hat und genau diese aktive Zeit ist lange vorbei!)

Und nun? Kannst Du Deinen Kritikpunkt nun irgendwie belegen? Oder wieder mal eine unbelegte Aussage, die alle Anderen als Tatsache entgegen nehmen müssen?
 
Blender3D

Blender3D

Gewagt ist auch ein geflügeltes Wort... etwas wischi waschi... Du weißt aber schon, das Dijkstra seit geraumer Zeit nicht mehr unter den Lebenden weilt?
Ist dir klar, dass das ein redundantes Statement ist. Du wiederholst die Information von @kneitzel und belehrst in damit.
:oops:
Also wenn Deine Logik so funktioniert, muss man Tipps von Dir noch genauer hinterfragen.
 
T

Tobias-nrw

break == goto statement ist (für mich) keine unzulässige Äquivalenz. *schulterzuck*
 
M

mrBrown

Der Link zu Dijkstra ist mir bekannt. Du solltest diesen aber lesen, da geht es um GOTO also einen Sprung. Das hat mit break gar nichts zu tun. Der Unterschied ist Dir aber scheinbar nicht geläufig.
Ersetz das break einfach durch ein return, dann ist er sicher zufriedengestellt :p

(Im Byte-Code entspricht ein break btw einem goto. Die Punkte von Dijkstra dürften für breaks in Verbindung mit Labeln genauso gelten)
Java:
private int labels() {
    label:
    {
        label2:
        if (new Random().nextBoolean()) {
            if (new Random().nextBoolean()) {
                break label2;
            }
            return 17;
        } else {
            break label;
        }

        return 42;
    }
    return 0;
}
 
T

Tobias-nrw

Außerdem meinte ich damit, dass man nichtmehr irdische Personen schwer fragen kann, wie sie etwas gemeint haben. (Es sei denn, jemand versteht hier etwas von Okkultismus, aber das ist jetzt wirklich am Thema vorbei...)
 
K

kneitzel

break == goto statement ist (für mich) keine unzulässige Äquivalenz. *schulterzuck*
Damit sind dann für Dich also auch switch Statements, in denen break Statements vorkommen, nicht zulässig?

Ersetz das break einfach durch ein return, dann ist er sicher zufriedengestellt :p

(Im Byte-Code entspricht ein break btw einem goto. Die Punkte von Dijkstra dürften für breaks in Verbindung mit Labeln genauso gelten)
Dann ist aber auch gar nichts erlaubt, denn Sprünge (GOTO) sind ja generell das, was am Ende die CPU macht. Also wenn man die Trennung zwischen erstelltem Code und dem daraus generierten Code nicht hat, dann wird man nur noch einfache, gradlinige Software entwickeln können fürchte ich.
(Selbst ein Funktionsaufruf ist ja nur ein pushen von Werten auf den Stack gefolgt von einem Sprung. Und am Ende dann das herunternehmen von Werten vom Stack und dann ein Sprung zu der Rücksprungadresse....)
 
Blender3D

Blender3D

break == goto statement ist (für mich) keine unzulässige Äquivalenz. *schulterzuck*
Die Anmerkung für mich ist wirklich notwendig. Du brauchst augenscheinlich einen Nachschulung für die Bedeutung Gleichheit.
GOTO. erlaubt den Sprung an eine beliebig definierte Stelle im Code.
break unterbricht eine Schleife. Genau das passiert auch in Deiner schwerer zu lesenden Version der Methode tick() in Deiner while Schleife auch.
Der Punkt ist mit break kann ich nicht an eine beliebige Stelle im Code springen. --> break != goto
Benutzt hier ein Anfänger den Account (seit 2015) von @Tobias-nrw ?
 
M

mrBrown

break unterbricht eine Schleife. Genau das passiert auch in Deiner schwerer zu lesenden Version der Methode tick() in Deiner while Schleife auch.
Der Punkt ist mit break kann ich nicht an eine beliebige Stelle im Code springen. --> break != goto
Genauer springt break hinter einen gelabelten Block, auch unabhängig von Schleifen. Aber es erlaubt eben keine Rückwärtssprünge.
 
M

mrBrown

Dann ist aber auch gar nichts erlaubt, denn Sprünge (GOTO) sind ja generell das, was am Ende die CPU macht. Also wenn man die Trennung zwischen erstelltem Code und dem daraus generierten Code nicht hat, dann wird man nur noch einfache, gradlinige Software entwickeln können fürchte ich.
(Selbst ein Funktionsaufruf ist ja nur ein pushen von Werten auf den Stack gefolgt von einem Sprung. Und am Ende dann das herunternehmen von Werten vom Stack und dann ein Sprung zu der Rücksprungadresse....)
Na so war das nicht gemeint :p
 
K

kneitzel

Ersetz das break einfach durch ein return, dann ist er sicher zufriedengestellt :p
Ach ja: Und bei Methoden gibt es ja für manch einen auch die Regel, dass man nur ein return Statement am Ende haben darf. Also nichts mit return vorher oder so ... Wer das break; so massiv ablehnt, der wird dies wohl auch ablehnen.

Wobei ich diese Clean Code Regel ablehne und für nicht konstruktiv halte. Das führt dann zu schlecht lesbarem Code, weil man ständig if (..) { ... } else { ... } Blöcke bekommt.

Aber das ist dann auch ein anderes Thema.

Außerdem meinte ich damit, dass man nichtmehr irdische Personen schwer fragen kann, wie sie etwas gemeint haben. (Es sei denn, jemand versteht hier etwas von Okkultismus, aber das ist jetzt wirklich am Thema vorbei...)
Also DU hast lediglich mit dem Namen eines Toten etwas begründen wollen. Und Du kannst das jetzt nicht begründen, weil die Person tot ist? Aber Du musst doch irgendwas von der Person wissen, wenn Du der Meinung warst, dass dies ein Argument für diese Sache ist.

Ach bitte, schlechter Verlierer würd ich mal sagen. ;) @Blender3D
Ihm geht es ja nur um eine Erläuterung der Kritik. Nur weil Du Deine Kritik nicht belegen / erläutern kannst, ist @Blender3D nun der schlechte Verlierer?

Kann man mir diese Logik evtl. etwas erläutern?
 
M

mrBrown

Ach ja: Und bei Methoden gibt es ja für manch einen auch die Regel, dass man nur ein return Statement am Ende haben darf. Also nichts mit return vorher oder so ... Wer das break; so massiv ablehnt, der wird dies wohl auch ablehnen.

Wobei ich diese Clean Code Regel ablehne und für nicht konstruktiv halte. Das führt dann zu schlecht lesbarem Code, weil man ständig if (..) { ... } else { ... } Blöcke bekommt.

Aber das ist dann auch ein anderes Thema.
In Clean Code steht doch auch, dass mehrere returns durchaus besser sein können?

Some programmers follow Edsger Dijkstra’s rules of structured programming. Dijkstra said that every function, and every block within a function, should have one entry and one exit. Following these rules means that there should only be one return statement in a function, no break or continue statements in a loop, and never, ever, any goto statements.

While we are sympathetic to the goals and disciplines of structured programming, those rules serve little benefit when functions are very small. It is only in larger functions that such rules provide significant benefit.

So if you keep your functions small, then the occasional multiple return, break, orcontinue statement does no harm and can sometimes even be more expressive than the single-entry, single-exit rule. On the other hand, goto only makes sense in large functions, so it should be avoided.
(Bezieht sich sogar auf goto :p)
 
Blender3D

Blender3D

(Im Byte-Code entspricht ein break btw einem goto. Die Punkte von Dijkstra dürften für breaks in Verbindung mit Labeln genauso gelten)
Da ich auch Assemblerprogramme schreibe, weiß ich natürlich auch dass im Byte Code dort ein Sprung stattfindet.
Dasselbe passiert aber auch bei einer abweisenden while Schleife im Byte Code oder wenn man return verwendet.
Der Punkt ist, dass BREAK <> GOTO ist. Warum?
Ich kann mittels break nicht an eine beliebige Stelle Springen. Also Logik:
A hat Eigenschaft 1 und 2.
B hat Eigenschaft 1.
--> A <> B
Und Dijkstra spricht in seinem Link von GOTO.
 
T

Tobias-nrw

@Blender3D Spulen wir nochmal zurück bitte. Ich wollte damit nicht schreiben, Dein Code sei schlecht oder Ähnliches, aber eben auch nicht "fehlerfrei", bzw an der ein oder anderen Stelle hätte man ihn, zB in einem Review, anders schreiben können.

@ all : Ich kann nicht genau begründen, wie Dijkstra das evtl gemeint haben könnte. Aber @mrBrown hat ja bereits ein paar relevante Stellen herausgesucht...
 
M

mrBrown

Der Punkt ist, dass BREAK <> GOTO ist.
[...]
Und Dijkstra spricht in seinem Link von GOTO.
Ich hab nirgends das Gegenteil behauptet.

Wenn man jedes GOTO ablehnt (was Dijkstra nicht getan hat) - auch die, die nur für Vorwärtssprünge genutzt werden - muss man den gleichen Maßstab aber auch für break ansetzen.

Setzt man einen sinnvollen Maßstab an (und weicht auch von single-entry/single-exit ab, weclhes für return, break & continue ebenso gilt), sind breaks völlig Okay, aber sowas wie Vorwärtssprünge mit Goto als Ersatz für finally aber auch. (Als reines Sprachfeature ist es mMn besser als goto, da begrenztere Nutzungsmöglichkeiten)

Das die Aussage von @Tobias-nrw so wie sie da stand dämlich ist, ist völlig klar. Aber je nachdem, wie hart man Dinge auslegt, sprechen die Dinge, die gegen goto sprechen, auch gegen breaks.

Ich kann mittels break nicht an eine beliebige Stelle Springen
Wenn man Blöcke sehr missbraucht, kann man immerhin an fast jede Stelle springen, nur z.B. nicht in andere, fremde Blöcke... Ist dann allerdings noch schlimmer als goto für sowas zu benutzen...
 
Zuletzt bearbeitet:
W

White_Fox

Ich staune darüber, auf welche Abwege eine recht einfache Anfängeraufgabe führen kann.

Und welch ein Trubel um ein Statement, daß es im Hauptthemenbereich des Forums nicht gibt. ;)
 
T

Tobias-nrw

Danke @mrBrown für Deine Unterstützung. Das Zitat oder der Beleg zeigt ja das ich nicht unrecht hatte mit meinen "Kritik"punkten. ;)

Hier gab es nur ein break; aber besonders, wenn sich die break;s häufen, tut das dem Lesefluss nicht gut...

An dem Beispiel sieht man auch, dass seine Thesen keinesfalls "in die Jahre gekommen", vielmehr aktueller denn je sind.
 
T

Tobias-nrw

public void tick() { int time; int i = 0; do { time = (this.time = countUpValue(this.time, max)); i++; } while (time == 0 && i < max.length); }
Ich weiß jetzt wieder, warum ich heute Morgen die Variable time hinzunahm... Weil i in der Schleife erhöht wird und i-1 ein zusätzlicher und unübersichtlicher Rechenschritt ist.
 
mihe7

mihe7

Wobei ich diese Clean Code Regel ablehne und für nicht konstruktiv halte. Das führt dann zu schlecht lesbarem Code, weil man ständig if (..) { ... } else { ... } Blöcke bekommt.
Grundsätzlich versuche ich, mich schon daran zu halten, habe aber kein Problem mit Ausnahmen. Typisches Beispiel:

Java:
public boolean equals(Object o) {
    if (o == null || o == this || !(o instanceof MeineKlasse)) {
        return o == this;
    }

    MeineKlasse k = (MeineKlasse) o;
    return Objects.equals(this.value, k.value);
}
ist für mich am angenehmsten zu lesen. Andererseits schreibe ich for-each-Schleifen in der Regel nicht, wenn dies zu einem return/break führen würde (Ausnahmen gibt es immer).
 
K

kneitzel

Grundsätzlich versuche ich, mich schon daran zu halten, habe aber kein Problem mit Ausnahmen. Typisches Beispiel:

Java:
public boolean equals(Object o) {
    if (o == null || o == this || !(o instanceof MeineKlasse)) {
        return o == this;
    }

    MeineKlasse k = (MeineKlasse) o;
    return Objects.equals(this.value, k.value);
}
ist für mich am angenehmsten zu lesen. Andererseits schreibe ich for-each-Schleifen in der Regel nicht, wenn dies zu einem return/break führen würde (Ausnahmen gibt es immer).
Also es ist schon oft so, dass man bei Funktionen am Ende das Ergebnis zurück gibt, aber ich denke, dass es nicht selten vorkommt, dass man eben aus einer Menge etwas heraus suchen muss und dann natürlich die Suche abbricht.

Und da sehe ich nun als einzige Alternative, eben auf das for statement zu verzichten und es generell per while Schleife ablaufen zu lassen. Aus dem Code
Code:
for(SomeType elment: elements) { 
    ...
    if (someCondition) return someExpression;
}
wird dann halt etwas wie (Pseudocode im Browser hier entstanden
Code:
Iterator<SomeType> iterator = elements.getIterator();
SomeType result = null;
while (result==null && iterator.hasNext()) {
    SomeType element = iterator.next();
    ...
    if (someCondition) result=someExpression;
}
return result;
Oder man schwenkt natürlich auf streams um mit elements.stream().filter(...).First(); was bei halbwegs aktuellem Java (Java 8 Minimum) gehen würde. Nur bei komplexerem Filter bin ich mir jetzt nur nicht sicher, ob dies lesbar bleibt. (Aber vermutlich schon. Complexe Dinge würde ich in den anderen Varianten ja auch nicht in der Schleife ablaufen lassen sondern eben verlagern. Das wird da ja auch gelten...

Habe ich da jetzt etwas übersehen? Das mit der While Schleife würde ich erst einmal als schwerer lesbarer ablehnen. Das mit der Streams API sieht aber tatsächlich gut aus und zeigt, dass meine bisherige Haltung diesbezüglich evtl. durch die Einführung der Streams API anzupassen ist.
 
mihe7

mihe7

Das mit der While Schleife würde ich erst einmal als schwerer lesbarer ablehnen.
Jein. Es ist zweifelsohne mehr Code, der aus Sicht von Anfängern zunächst schwerer zu begreifen ist (Iterator-Konzept). Tatsächlich ist das aber alles Standard-Boilerplate-Code. Dieses Muster findet man also so gut wie immer, wenn mit Iteratoren gearbeitet wird.

Tatsächlich sagt das while aber wesentlich präziser, worum es geht: iteriere so lange, bis result != null ist. Das for-Each lügt mir dagegen ins Gesicht: es tut so, als ob über alle Elemente iteriert würde, tatsächlich bricht es irgendwann ab und springt auch noch aus der Methode.

Wenn man die Blöcke einklappt, wird deutlicher, was ich meine:
Java:
for (SomeType element : elements) { ... }
return null;
vs
Java:
while (result == null && it.hasNext()) { ... }
return result;
Für so etwas würde ich heute allerdings auch Streams verwenden :)

(EDIT: Schleifenbedingung korrigiert)
 
Zuletzt bearbeitet:
K

kneitzel

Jein. Es ist zweifelsohne mehr Code, der aus Sicht von Anfängern zunächst schwerer zu begreifen ist (Iterator-Konzept). Tatsächlich ist das aber alles Standard-Boilerplate-Code. Dieses Muster findet man also so gut wie immer, wenn mit Iteratoren gearbeitet wird.

Tatsächlich sagt das while aber wesentlich präziser, worum es geht: iteriere so lange, bis result != null ist. Das for-Each lügt mir dagegen ins Gesicht: es tut so, als ob über alle Elemente iteriert würde, tatsächlich bricht es irgendwann ab und springt auch noch aus der Methode.
Diese Argumentation kann ich nachvollziehen, aber wirklich überzeugt bin ich nicht. :)
Im Augenblick empfinde ich die While Scheife auf Grund des Boilerplate Codes eher als unleserlicher. Aber auch der logische Ablauf ist bei der while schleife deutlicher, da hier eine Bedingung ist. Bei der for Schleife hat man das ja unterteilt in einmal die Definition über was iteriert werden soll + einer if Abfrage innen drin. (if (Bedingung) return result; ist ja existenziell.

Aber ich werde auf jeden Fall einmal in der Praxis schauen, ob die while schleife deutlicher wird (Ist ja auch eine Frage der Gewöhnung) und ich werde auch einmal den Smalltalk im Team darauf lenken. (Akzeptiert sind da beide Wege.)
 
K

kneitzel

Du vergleichst Äpfel und Birnen... Es ging nicht um ein return; sondern um das noch schädlichere break;.

Und naja, da bleibt nur zu hoffen, dass Du nicht etwas ablehnen kannst...
Bitte lies einen Thread, ehe Du antwortest. In den letzten Beiträgen (ab #33) ging es eben nicht mehr um das "break;" oder "return" sondern um die Konsequenzen. Und da gab es im Thread drei Optionen für die Implementierung: for-each, while und Streams.

Und genau die wurden bewertet. Da kannst Du auch gerne Deine Sicht sagen.

@mihe7 Interessant ist, wenn "null" ein mögliches Resultat wird und bei nicht Vorhandensein eines Resultates eine Exception geworfen werden soll. Das verkompliziert die while-Lösung etwas. Da wäre mein Ansatz dann, Optional<ResultTyp> zu verwenden. Dann kann man halt result.isPresent in der while Bedingung und ein abschließendes if nach der while schleife...

Da wird das mit der Stream API doch deutlich einfacher und übersichtlicher.
 
mihe7

mihe7

Diese Argumentation kann ich nachvollziehen, aber wirklich überzeugt bin ich nicht.
Das sind ja auch nur persönliche Präferenzen - und die sind nun einmal individuell verschieden :)

Interessant ist, wenn "null" ein mögliches Resultat wird und bei nicht Vorhandensein eines Resultates eine Exception geworfen werden soll. Das verkompliziert die while-Lösung etwas.
Darin unterscheiden sich while und for-each aber nicht - oder ich habe Dich falsch verstanden.
 
K

kneitzel

Darin unterscheiden sich while und for-each aber nicht - oder ich habe Dich falsch verstanden.
Das Problem, welches ich meine ist: Angenommen es kann auch null als Ergebnis heraus kommen. Also ich habe etwas wie:
Code:
for (Element element: elements) {
  if (someCheck(element)) return element.getValue();
}
// Hier ist klar: element wurde nicht gefunden - kann also Exception werfen oder default Wert zurück geben oder oder oder ...
Die 1:1 Übersetzung hinkt aber:
Code:
while (result == null && it.hasNext()) {
  Element element = it.next();
  if (someCheck(element)) result = element.getValue();
}
Aber da bricht dann ja auch noch nicht einmal die Schleife ab, wenn getValue() auch null liefern kann/darf.

Aber das war ein Denkfehler bei mir. Denn wenn man es aufschreibt, dann wird klar: ich kann ja einfach der element in result speichern. Das ist dann nicht null und ich kann am Ende auch schauen: Wenn result == null, dann gebe ich default Wert zurück oder werfe eine Exception ... also gar kein Thema, das zu diskutieren wäre und auch nichts, das neue Argumente in die Diskussion einbringt. Also an der Stelle sorry für evtl. Verwirrungen.
 
Thema: 

GOTO (aus Digitaluhr, Hilfe !)

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben