# Exceptions (try/catch)



## recess (31. Okt 2012)

Hallo zusammen, ich habe hier eine Methode geschrieben, die Lottozahlen zwischen 1 und 49 einlesen soll.
Dabei möchte ich zumindest eine  Exception machen, dass ein Fehler auftaucht wenn ein Buchstabe eingegeben wird statt einer Zahl und am liebsten noch wenn die Eingabe außerhalb von 1 und 49 ist.
Mein jetziger stand ist:


```
public void tippabgabe(){

		boolean getippt;
		int[] tipp = new int[6];
		try {
			for (int i=0; i<6; i++){
				System.out.print("Geben Sie nun die "+(i+1)+". Zahl ein!: ");
				do {
					getippt = false;
					tipp[i] = eingabe.nextInt();

					for (int j = 0; j < i; j++) {
						if (tipp[j] == tipp[i]){
							getippt = true;
							System.out.print("Diese Zahl wurde bereits getippt, wählen Sie bitte eine neue Zahl!:");
						}
					}
				} while (getippt);
			}
		}catch (InputMismatchException e){	
			System.out.print("Die Eingabe ist ungültig. Bitte geben Sie nun eine Zahl in:");
			eingabe = new Scanner(System.in); 	
		}// end of catch

		java.util.Arrays.sort( tipp );
		objektTippSpeicher.speichere(new Tipp(tipp));
	}
```

Allerdings kommt dann bei mir wenn ich dann einen Buchstaben eingebe, die Fehlermeldung (Du hast keine zahl eingegeben(oder so ähnlich)" und danach kommt direkt bitte treffen Sie eine Auswahl , springt also raus.
Ich steh auf dem Schlauch, ich muss warsch nur die Klammern anders setzen, aber bin der meinung alles ausprobiert zu haben,.
Kann mir vll jemand nen Tipp geben?


----------



## hüteüberhüte (1. Nov 2012)

Ich hätts so gemacht:

```
private static int[] lottozahlenEinlesen() throws IOException {
        int[] result = new int[6];
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        for (int i = 0; i < result.length;) {
            try {
                System.out.print(i + 1 + ". Zahl eingeben: ");
                int input = Integer.parseInt(reader.readLine());
                if (input < 1 || input > 49) {
                    throw new NumberFormatException();
                }
                for (int j = 0; j < i; j++) {
                    if (result[j] == input) {
                        throw new NumberFormatException();
                    }
                }
                result[i++] = input;
            } catch (NumberFormatException nfe) {
                System.out.println("Bitte Zahl zwischen 1 und 49 eingeben.");
            }
        }
        Arrays.sort(result);
        return result;
    }

    public static void main(String[] args) throws IOException {
        System.out.println(Arrays.toString(lottozahlenEinlesen()));
    }
```

Wenn keine Zahl eingegeben wird, kleiner 1 oder größer 49, oder bereits vorhanden ist, wird eine NFE geworfen/ausgelöst und behandelt.

Hth!


----------



## KuhTee (1. Nov 2012)

Hm... hüteüberhüte... irgendjemand sollte die IOException schon behandeln...
Statt dem Array würde sich ein Set anbieten, dann sparst du dir das drüber Iterieren zum Prüfen.

Und ehrlich gesagt würde ich das Verwenden einer Exception in diesem Fall eher als schlechten (und vor allem unnötig umständlichen) Code ansehen. Ist mir schon klar, dass soll zur Übung sein. Aber eher unschönen Code üben? Ich weiss nicht... Missbrauchen von Exceptions sollte man eher nicht üben, finde ich.


----------



## TKausL (1. Nov 2012)

KuhTee hat gesagt.:


> Hm... hüteüberhüte... irgendjemand sollte die IOException schon behandeln...
> Statt dem Array würde sich ein Set anbieten, dann sparst du dir das drüber Iterieren zum Prüfen.
> 
> Und ehrlich gesagt würde ich das Verwenden einer Exception in diesem Fall eher als schlechten (und vor allem unnötig umständlichen) Code ansehen. Ist mir schon klar, dass soll zur Übung sein. Aber eher unschönen Code üben? Ich weiss nicht... Missbrauchen von Exceptions sollte man eher nicht üben, finde ich.



1. Das Set tut intern genau das selbe, drüberiterieren...
2. Warum sollte diese Exception "Missbraucht" worden sein? Sie tut genau das was sie soll: Einen Fehler melden. Nur weil die Exception in der selben Methode behandelt wird ist sie noch lange nicht "Missbraucht".


----------



## hüteüberhüte (1. Nov 2012)

KuhTee hat gesagt.:


> Hm... hüteüberhüte... irgendjemand sollte die IOException schon behandeln...



Unsinn, wenn eine IOE auftritt, soll das Programm beendet werden. So einfach ist das.



KuhTee hat gesagt.:


> Statt dem Array würde sich ein Set anbieten, dann sparst du dir das drüber Iterieren zum Prüfen.



Ich denke, mit einem Set & einer Hashfunktion kann jemand, der gerade bei einem int[] ist, noch nicht so viel anfangen. Wenn schon, dann wäre ein zusätzliches boolean[] jedenfalls schneller.



KuhTee hat gesagt.:


> Und ehrlich gesagt würde ich das Verwenden einer Exception in diesem Fall eher als schlechten (und vor allem unnötig umständlichen) Code ansehen. Ist mir schon klar, dass soll zur Übung sein. Aber eher unschönen Code üben? Ich weiss nicht... Missbrauchen von Exceptions sollte man eher nicht üben, finde ich.



Du würdest also nie 
	
	
	
	





```
throw new SomeException
```
 schreiben, oder wie darf ich das verstehen? Meine Methode ist einwandfrei, du darfst Deinen Neid gerne behalten.


----------



## tfa (1. Nov 2012)

Ich denke, hier ist der Missbrauch der NumberFormatException zum Kontrollfluss gemeint (Zeile 9 und 13). Und das ist ziemlich daneben. Zumal im catch-Block einfach nur eine Meldung ausgegeben wird. Das kann man auch in eine Methode auslagern.


----------



## KuhTee (1. Nov 2012)

TKausL hat gesagt.:


> 1. Das Set tut intern genau das selbe, drüberiterieren...


Und warum dann das nicht nutzen statt selbst zu schreiben? 



TKausL hat gesagt.:


> 2. Warum sollte diese Exception "Missbraucht" worden sein? Sie tut genau das was sie soll: Einen Fehler melden. Nur weil die Exception in der selben Methode behandelt wird ist sie noch lange nicht "Missbraucht".


Weil die Exception in dem Fall zur Programmflusssteuerung verwendet wird, was unnötig ist (siehe unten). Ein Grund, weshalb Java als so geschwätzig gilt, ist der exzessive und oftmals unnötige Einsatz von Exceptions. Dass die Art und Weise der Implementierung und Verwendung von Exceptions in Java nicht gerade optimal ist, sehen ja auch erfahrene Javaentwickler so. Und ich finde einfach, dass man Neulingen das durchaus frühzeitig so erklären sollte. Hab schon genug Kollegen, wo man vor try-catch den wichtigen Code nicht mehr sieht.



hüteüberhüte hat gesagt.:


> Unsinn, wenn eine IOE auftritt, soll das Programm beendet werden. So einfach ist das.


Ok, sagen wir: Ansichtssache. Sowie: war nur ein Vorschlag.



hüteüberhüte hat gesagt.:


> Du würdest also nie
> 
> 
> 
> ...


Doch, natürlich würde ich das. Aber bei einer "Ausnahme" eben. Nur in dem Code oben würde ich eben keine Exception verwenden, weil mMn "Missbrauch" von Exceptions. Eher so (ohne Garantie auf Kompilerbarkeit) :

```
private static int[] lottozahlenEinlesen() throws IOException {    
    Set<Integer> result = new HashSet<>();        
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    while (result.size() < 6) {
        System.out.print((result.size() + 1) + ". Zahl eingeben: ");
        int input = Integer.parseInt(reader.readLine());
        if (input < 1 || input > 49 || result.contains(input)) {
            System.out.println("Bitte Zahl zwischen 1 und 49 eingeben.");
        }
        else {
            result.add(input);
        }
    }
    return Arrays.sort(result.toArray());
}
 
public static void main(String[] args) throws IOException {
    System.out.println(Arrays.toString(lottozahlenEinlesen()));
}
```
Ist mMn lesbarer, vor allem da deine Exception ja nichts weiter darstellt als ein "if (...) goto ...".

@tfa: Exakt. Leider wird das häufig sogar genau so gelehrt.


----------



## tfa (1. Nov 2012)

KuhTee hat gesagt.:


> Und warum dann das nicht nutzen statt selbst zu schreiben?



Wenn man wie du ein HashSet benutzt, itiert es natürlich nicht über alle Einträge. Das ist ja gerade der Witz eines Sets.


----------



## KuhTee (1. Nov 2012)

tfa hat gesagt.:


> Wenn man wie du ein HashSet benutzt, itiert es natürlich nicht über alle Einträge. Das ist ja gerade der Witz eines Sets.


Es geht dabei ja nur darum, eine Zahl nicht mehrfach einzulesen. Und dafür eben das (Hash)Set. Drüber"iterieren" tut da in dem Fall nix, ja, aber es geht ja nur darum, sich das manuelle Iterieren zu sparen. Was es fürn Set ist, ist in dem Fall ja sowieso eher unwichtig.


----------



## hüteüberhüte (1. Nov 2012)

KuhTee hat gesagt.:


> Eher so (ohne Garantie auf Kompilerbarkeit) :



Das kompiliert nicht, weil du eine mögliche NFE weder behandelst noch weitergibst. Und sobald du dich darum kümmern würdest, wäre dein Code mindesten genauso verschachtelt wie meiner: Ergo, unübersichtlicher.


----------



## KuhTee (1. Nov 2012)

Mea culpa, hast recht. Das kommt davon, wenn man in Qt denkt und dann Java schreibt 
Es ging mir bei meiner Kritik aber um 
	
	
	
	





```
throw new NumberFormatException();
```
, wo ich bei meiner Aussage bleibe: Missbrauch von Exceptions.


----------



## hüteüberhüte (1. Nov 2012)

KuhTee hat gesagt.:


> Missbrauch von Exceptions.



In dieser Methode geht es nicht um Schnelligkeit. Osi layer 8 ist sowieso der langsamste Part. Einen Missbrauch kann ich nicht erkennen.

Du müsstest um Zeile 6-12 einen Try-catch-Block schreiben, damit dein Programm funktioniert.


----------



## tfa (1. Nov 2012)

hüteüberhüte hat gesagt.:


> In dieser Methode geht es nicht um Schnelligkeit. Osi layer 8 ist sowieso der langsamste Part. Einen Missbrauch kann ich nicht erkennen.
> 
> Du müsstest um Zeile 6-12 einen Try-catch-Block schreiben, damit dein Programm funktioniert.



Es bleibt dabei: ein eklatanter Missbrauch von Exceptions zu Kontrollflusszwecken! Wer über das Anfängerstadium hinaus ist, sollte sowas nicht mehr machen.

Das Gewurschtel in diesem Beispiel könnte man vermeiden, in dem man einfach mehr und kleinere Methoden macht: eine zum Einlesen einer (gültigen Zahl), eine zum Testen des Bereichs, zum Testen auf Doppelte etc. Nur so wird es übersichtlich.


----------



## hüteüberhüte (1. Nov 2012)

tfa hat gesagt.:


> Das Gewurschtel in diesem Beispiel könnte man vermeiden, in dem man einfach mehr und kleinere Methoden macht: eine zum Einlesen einer (gültigen Zahl), eine zum Testen des Bereichs, zum Testen auf Doppelte etc. Nur so wird es übersichtlich.



Naja, dann mach das doch, anstatt hier andere zu kritisieren. Ich möchte gerne mal sehen, inwieweit das dann übersichtlicher wäre. :lol:


----------



## KuhTee (1. Nov 2012)

hüteüberhüte hat gesagt.:


> In dieser Methode geht es nicht um Schnelligkeit.


Schnelligkeit war nun wirklich das Letzte, was mir dabei in den Sinn kam.



hüteüberhüte hat gesagt.:


> Einen Missbrauch kann ich nicht erkennen.


Wurde doch schon genannt: du verwendest Exceptions zur Programmflusssteuerung, was in diesem Fall keinen Sinn macht und was man so Neulingen auch gar nicht erst beibringen sollte.



hüteüberhüte hat gesagt.:


> Du müsstest um Zeile 6-12 einen Try-catch-Block schreiben, damit dein Programm funktioniert.


Ja (also eigentlich nur um das parseInt()). Mach ich jetzt aber nicht, ok? 
Bzw., in einem "echten" Programm würde ich natürlich eine parseInteger Methode verwenden, die keine (in vielen Fällen sowieso unnütze) Exception wirft. Das meinte ich weiter oben mit "in Qt gedacht", da liefert die entsprechende Methode bei einer Nicht-Zahl nämlich schlichtweg 0 (die Info über einen Fehler ist optional). Das geht jetzt aber schon langsam in Richtung einer Diskussion über Sinn und Unsinn von Exceptions, was wohl hier eh fehl am Platze ist. Ich wollte nur darauf hinweisen, dass das Werfen einer Exception in diesem Fall als eher schlechter Code zu verstehen ist und vermieden werden sollte, besonders in größeren Programmen.


----------



## KuhTee (1. Nov 2012)

hüteüberhüte hat gesagt.:


> Ich möchte gerne mal sehen, inwieweit das dann übersichtlicher wäre. :lol:


Hm, wieso sollte es nicht? Das Aufteilen einer Methode in mehrere kleinere, die exakt eine einzige Aufgabe haben (in etwa genau die, die tfa genannt hat) ist doch der absolut übliche Weg, um die Übersichtlichkeit in einem Programm zu verbessern. Mal abgesehen davon, dass man die Methoden mehrfach verwenden kann, was sich ja gerade beim Exception-freien Parsen eines String anbietet.


----------



## hüteüberhüte (1. Nov 2012)

KuhTee hat gesagt.:


> Ja (also eigentlich nur um das parseInt()). Mach ich jetzt aber nicht, ok?



Ich hab mir mal erlaubt, deine Methode zu verbessern:

```
private static int[] lottozahlenEinlesen() throws IOException {
        Set<Integer> result = new HashSet<Integer>();
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while (result.size() < 6) {
            System.out.print(result.size() + 1 + ". Zahl eingeben: ");
            try {
                int input = Integer.parseInt(reader.readLine());
                if (input < 1 || input > 49 || result.contains(input)) {
                    System.out.println("Bitte Zahl zwischen 1 und 49 eingeben.");
                } else {
                    result.add(input);
                }
            } catch (NumberFormatException nfe) {
                System.out.println("Bitte Zahl zwischen 1 und 49 eingeben.");
            }
        }
        int[] a = new int[result.size()];
        Iterator<Integer> iter = result.iterator();
        int i = 0;
        while (iter.hasNext()) {
            a[i++] = iter.next();
        }
        Arrays.sort(a);
        return a;
    }
```

Sieht wahnsinnig übersichtlich aus in Vergleich zu meiner Methode... [/IRONIE] Das Thema ist für mich erledigt. Schönen Abend noch


----------



## recess (1. Nov 2012)

Hey ich wollte jetzt eig kein so großes fass aufmachen :lol:

Es ging mir darum, wie ich die Exception handle. DIe bisherigen Lösungen von euch sehen alle vor, dass ich unten drunter eine Main.Methode habe. Die habe ich aber schon:/
Kann man das nich einfach (es muss nich sauber programmiert sein), innerhalb der Methode , nur mit Exception wie IOException und try catch irg wie abfangen ? Muss man die unbedingt werfen? WEnn es eine möglichkeit gibt das ohne throw zu machen, wie geht diese?






hüteüberhüte hat gesagt.:


> Ich hab mir mal erlaubt, deine Methode zu verbessern:
> 
> ```
> private static int[] lottozahlenEinlesen() throws IOException {
> ...


----------



## Spacerat (1. Nov 2012)

recess hat gesagt.:


> Hey ich wollte jetzt eig kein so großes fass aufmachen :lol:


Dann bleib' halt bei den halben Litern. :lol:

```
import java.util.Arrays;
import java.util.Scanner;

public class Lotto {
	private static final int[] BALLS;

	static {
		BALLS = new int[49];
		for(int n = 0; n < BALLS.length; n++) {
			BALLS[n] = n + 1;
		}
	}

	public static void main(String[] args) {
		int[] balls = BALLS.clone();
		int[] tipp = new int[6];
		Scanner sc = new Scanner(System.in);
		int zahl = 0;
		for(int n = 0; n < 6; n++) {
			try {
				zahl = sc.nextInt();
			} catch (InputMismatchException e) {
				sc.next();
				System.out.println("ZAHLEN! Keine Buchstaben! Nochmal");
				n--;
				continue;
			}
			if(!pruefeZahl(zahl, balls)) {
				System.out.println("Zahl ungueltig oder schon getippt. Nochmal");
				n--;
			} else {
				tipp[n] = zahl;
			}
		}
		sc.close();
		System.err.print("Ihr Tipp: " + Arrays.toString(tipp));
	}

	private static boolean pruefeZahl(int zahl, int[] balls) {
		Arrays.sort(balls);
		int index = Arrays.binarySearch(balls, zahl);
		if(zahl < 0 || index < 0) {
			return false;
		}
		balls[index] = -1;
		return true;
	}
}
```
Ich denke mal, Methodenname und Rückgabewert bekommst du noch selber hin.


----------



## recess (1. Nov 2012)

Aber du hast doch jetzt auch alles wieder in die Main Methode gepackt?! Kann ich die dann einfach umbennnen und neuen Rückgabewert bilden?


----------



## recess (1. Nov 2012)

Hier ist meine komplette Klasse:

Vielleicht hilft das mich besser zu verstehen 


```
package Lottozahlen;

import java.util.Scanner;

public class LottoMain {
	//Anlegen vom Scanner und von Objekten des Types verschiedener Klassen (für Methoden aufruf oder ähnliches)
	private static Scanner eingabe = new Scanner (System.in);
	ZiehungsSpeicher objektZiehungsSpeicher = new ZiehungsSpeicher();
	Lottotrommel objektLottotrommel = new Lottotrommel();
	TippsSpeicher objektTippSpeicher = new TippsSpeicher();
	Tipptrommel objektTipptrommel = new Tipptrommel();

	//Main-Methode. Ausgabe der einzelnen Menüpunkte auf die Console.
	//Außerdem wird mit Hilfe des Lottomain-Objektes die Methode auswahl() aufgerufen.
	public static void main(String[] args) {
		//Erstellen eines Objektes der Klasse Lottomain
		LottoMain objektLottoMain = new LottoMain ();

		System.out.println("Willkommen in Ihrem Lottoprogramm");
		System.out.println("--------------------");
		System.out.println("(1) Ich will n-Ziehungen automatisch (per Zufall) generieren lassen, dabei n selber vorgeben!");
		System.out.println("(2) Ich will 1000 Ziehungen generieren und abspeichern lassen!");
		System.out.println("(3) Ich will alle Ziehungen (Lottozahlen) ausgeben!");
		System.out.println("(4) Ich möchte einen Tipp abgeben!");
		System.out.println("(5) Ich will n-Tipps automatisch (per Zufall) generieren lassen, dabei n selber vorgeben!");
		System.out.println("(6) Ich möchte alle Tipps ausgeben!");
		System.out.println("(7) Ich will das Programm beenden!");
		System.out.println();
		//Aufruf der Methode auswahl()
		objektLottoMain.auswahl();
	}	


	//Methode, die das Menü wiederspiegelt. Der Benutzer wird aufgefordert 
	//sich zu entscheiden, welchen Menü-Punkt er auswählen möchte.
	//Die unterschiedlichen Methoden werden mit Hilfe von switch-case realisiert.
	//Wenn der Benutzer den Menüpunkt 7 wählt, wird der "else"-Teil aufgerufen
	//und die do Schleife abgebrochen.
	
	public void auswahl (){
		int auswahl;
		do{
			System.out.print("Treffen Sie nun eine Auswahl: ");
			auswahl= eingabe.nextInt();

			if (auswahl !=7){
				switch (auswahl){
				case 1: nZiehungen();
				break;
				case 2: tausendZiehung();
				break;	
				case 3: System.out.println(objektZiehungsSpeicher);
				break;
				case 4: tippabgabe();
				break;
				case 5: nTipps();
				break;
				case 6:  System.out.println(objektTippSpeicher);
				break;
				default: System.out.println("Falsche Eingabe");
				}
				System.out.println();
			}
			else{		 
				System.out.println();
				System.out.println("~~~~~~~~~~~~~~~~~");
				System.out.println("Auf Wiedersehen, ich hoffe Sie sind jetzt reich!");
			}
		}while (auswahl !=7);
	}


	//Methode zur automatischen Generierung von 1000 Lottozahlen.
	//Die Lottozahlen werden durch den Teil "objektLottotrommel.trommeldrehen()"
	//gezogen und durch den ersten Teil "objektZiehungsSpeicher.speichere()" über 
	//die Methode speichere() abgespeichert. trommeldrehen() ist eine Methode vom Typ Ziehung
	// und die Methode speichere() verlangt den Typ Ziehung.
	private void tausendZiehung (){
		for (int i = 0; i <1000 ; i++) {
			objektZiehungsSpeicher.speichere(objektLottotrommel.trommeldrehen());
		}
	}


	//Methode zur automatischen Generierung von n Lottozahlen. "n" soll durch den 
	//Benutzer selbst entschieden werden.
	//Die Lottozahlen werden durch den Teil "objektLottotrommel.trommeldrehen()"
	//gezogen und durch den ersten Teil "objektZiehungsSpeicher.speichere()" über 
	//die Methode speichere() abgespeichert. trommeldrehen() ist eine Methode vom Typ Ziehung
	// und die Methode speichere() verlangt den Typ Ziehung.
	private void nZiehungen(){
		System.out.print("Wie viele Lottozahlen wollen Sie generieren? ");
		int durchläufe=eingabe.nextInt();
		for (int i = 0; i <durchläufe ; i++) {
			objektZiehungsSpeicher.speichere(objektLottotrommel.trommeldrehen());
		}
	}

	//Methode zur automatischen Generierung von n Tipps. "n" soll durch den 
	//Benutzer selbst entschieden werden.
	//Die Tipps werden durch den Teil "objektTipptrommel.tipptrommeldrehen()"
	//gezogen und durch den ersten Teil "objektTippSpeicher.speichere()" über 
	//die Methode speichere() abgespeichert. tipptrommeldrehen() ist eine Methode vom Typ Tipp
	//und die Methode speichere() verlangt den Typ Tipp.
	
	private void nTipps(){
		System.out.print("Wie viele Tipps wollen Sie generieren? ");
		int durchläufe=eingabe.nextInt();
		for (int i = 0; i <durchläufe ; i++) {
			objektTippSpeicher.speichere(objektTipptrommel.tipptrommeldrehen());
		}
	}


	public void tippabgabe(){

		boolean getippt;
		int[] tipp = new int[6];

		for (int i=0; i<6; i++){
			System.out.print("Geben Sie nun die "+(i+1)+". Zahl ein!: ");
			do {
				getippt = false;
				tipp[i] = eingabe.nextInt();
				if (tipp[i] < 1 || tipp[i] > 49) {
					getippt =true ;
					System.out.print("Die Zahl befindet sich außerhalb von 1 und 49! Geben Sie eine neue Zahl ein: ");
				}

				for (int j = 0; j < i; j++) {
					if (tipp[j] == tipp[i]){
						getippt = true;
						System.out.print("Diese Zahl wurde bereits getippt, wählen Sie bitte eine neue Zahl!:");
					}
				}
			}while (getippt);

		}
		java.util.Arrays.sort( tipp );
		objektTippSpeicher.speichere(new Tipp(tipp));
	}

}
```


----------



## hüteüberhüte (1. Nov 2012)

Zuguterletzt:

```
private static int[] lottozahlenEinlesen() throws IOException {
        int[] result = new int[6];
        boolean[] contai = new boolean[50];
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        for (int i = 0; i < result.length;) {
            System.out.print(i + 1 + ". Zahl eingeben: ");
            try {
                int input = Integer.parseInt(reader.readLine());
                if (input < 1 || input > 49 || contai[input]) {
                    System.out.println("Bitte Zahl zwischen 1 und 49 eingeben.");
                } else {
                    contai[input] = true;
                    result[i++] = input;
                }
            } catch (NumberFormatException nfe) {
                System.out.println("Bitte Zahl zwischen 1 und 49 eingeben.");
            }
        }
        Arrays.sort(result);
        return result;
    }
```

Finde ich nur nicht so schön. Die NFE steuert eben doch den Programmfluss und ist ohne Scanner auch nicht vermeidbar.

@Spacerat: Scanner schließen? :autsch:


----------



## recess (1. Nov 2012)

und in der letzten Methode will ich eine Exception einbauen, aber wie man sieht klappt Sie nicht :lol:


----------



## Spacerat (1. Nov 2012)

hüteüberhüte hat gesagt.:


> @Spacerat: Scanner schließen? :autsch:


Du sprichst mir aus der Seele, aber ich mag halt keine Warnings.
@TO: Methodenname und Rückgabewert ändern ist doch 'n Klacks.

```
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.Scanner;

public class Lotto {
	private static final int[] BALLS;

	static {
		BALLS = new int[49];
		for(int n = 0; n < BALLS.length; n++) {
			BALLS[n] = n + 1;
		}
	}

	public static int[] lottoTipp() {
		int[] balls = BALLS.clone();
		int[] tipp = new int[6];
		Scanner sc = new Scanner(System.in);
		int zahl = 0;
		for(int n = 0; n < 6; n++) {
			try {
				zahl = sc.nextInt();
			} catch (InputMismatchException e) {
				sc.next();
				System.out.println("ZAHLEN! Keine Buchstaben! Nochmal");
				n--;
				continue;
			}
			if(!pruefeZahl(zahl, balls)) {
				System.out.println("Zahl ungueltig oder schon getippt. Nochmal");
				n--;
			} else {
				tipp[n] = zahl;
			}
		}
		sc.close();
		return tipp;
	}

	private static boolean pruefeZahl(int zahl, int[] balls) {
		Arrays.sort(balls);
		int index = Arrays.binarySearch(balls, zahl);
		if(zahl < 0 || index < 0) {
			return false;
		}
		balls[index] = -1;
		return true;
	}
}
```


----------



## recess (1. Nov 2012)

Super, es funktioniert vielen Dank 

Allerdings Blicke ich durch die obere und untere Methoden nicht durch.
ALso 


```
private static final int[] BALLS;

	static {
		BALLS = new int[49];
		for(int n = 0; n < BALLS.length; n++) {
			BALLS[n] = n + 1;
		}
	}
```


```
private static boolean pruefeZahl(int zahl, int[] balls) {
		Arrays.sort(balls);
		int index = Arrays.binarySearch(balls, zahl);
		if(zahl < 0 || index < 0) {
			return false;
		}
		balls[index] = -1;
		return true;
	}
```
#
Vielleicht kannst du mir kurz erklären was die Methoden machen genau? Was macht die Methode binary Search??

Wieso einfach nur "static{...}", Ist das eine Methode oder wie?


----------



## Spacerat (1. Nov 2012)

Das static ist ein Klasseninitializer (eine Methode, ja). Dieser wird einmalig beim initialisieren der Klasse aufgerufen. In diesem Initializer definiere ich einen Satz Lottokugeln (BTW.: Ein Array eignet sich für soetwas normalerweise weniger, diese Idee also gleich vergessen ), der Klassenweit verfügbar ist, also in jeder Instanz dieser Klasse der selbe (Bemerkung: Arrayinhalte könnten von jeder Instanz beliebig verändert werden).

Der erste Schritt beim Lottotipp ist, den Klassenweiten Kugel-Satz zu kopieren, damit er verändert werden kann ("BALLS.clone()"). Schon getippte Zahlen werden mit einem negativen Wert (-1) invalidiert und so von der Binärsuche nicht mehr erfasst (besser, man hätte "zahl < 1" ausgewertet und die getippte Zahl mit 0 invalidiert, aber das ist nur ein gewohnheitsbedingter Schönheitsfehler).
Die Binärsuche prüft, ob die getippte Zahl noch in dem aktuellen Satz Kugeln vorhanden ist. Das geht aber nur, wenn bereits getippte Zahlen in diesem Satz einen invaliden Wert bekommen.


----------



## KuhTee (1. Nov 2012)

hüteüberhüte hat gesagt.:


> Sieht wahnsinnig übersichtlich aus in Vergleich zu meiner Methode... [/IRONIE] Das Thema ist für mich erledigt. Schönen Abend noch


Es sieht so wahnsinnig übersichtlich aus, weil der Code wahnsinnig schlecht ist 
Naja, Scherz. Aber wieder eben unnötig umständlich (schon wieder Iterieren, warum bist du so geil auf Iterieren?) und die Ratschläge von tfa (sinnvolles Aufteilen auf Methoden) hast du auch nicht berücksichtigt.

[EDIT]
Wie wärs denn beispielsweise so? Man kann natürlich das Prüfen der Zahl auch noch in eine Methode auslagern:

```
private static int parseInt(String s)
{
	try {
		return Integer.parseInt(s);
	}
	catch (NumberFormatException e) {} //In dem Fall ist die Exception eher nutzlos -> 0
	return 0;
}

private static int[] lottozahlenEinlesen() throws IOException {    
    Set<Integer> result = new HashSet<>();        
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    while (result.size() < 6) {
        System.out.print((result.size() + 1) + ". Zahl eingeben: ");
        int input = parseInt(reader.readLine());		
        if (input < 1 || input > 49) {
            System.out.println("Bitte Zahl zwischen 1 und 49 eingeben.");
        } else if (result.contains(input)) {
            System.out.println("Zahl schon verwendet.");
        } else {
            result.add(input);
        }
    }
    return Arrays.sort(result.toArray());
}
 
public static void main(String[] args) throws IOException {
    System.out.println(Arrays.toString(lottozahlenEinlesen()));
}
```
Keine wilden Programmsprünge, Code ist von oben nach unten durchgehbar und verständlich. Klar kann man noch weitere Methoden nutzen zB eine eigene Methode die sich nur ums Einlesen kümmert und eine nur fürs Validieren, aber dann wirds wirklich langsam "Kanonen auf Spatzen". Und schau mal: obwohl der Code genauere Fehlermeldungen zeigt als deiner, ist er nicht länger. Cool, wa? 

[/EDIT]



recess hat gesagt.:


> Allerdings Blicke ich durch die obere und untere Methoden nicht durch.


Mit dem oberen Codeblock wirds "BALLS" mit 1 bis 49 automatisch initialisiert, der untere Codeblock "löscht" eine Kugel aus BALLS. Also unglaublich umständliches Vorgehen für ein triviales Problem, aber scheinbar scheinen beide auf sowas zu stehen...


----------



## recess (1. Nov 2012)

Sorry , aber ich verstehe immernoch bahnhof.
Die Methode lottoTipp ist klar, Das ist kein Problem.
Aber vielleicht kannst du nochmal auf 2 Fragen eingehen:

1.) Was GENAU wird in static gemacht, wieso wählst du diese Form einfach nur static zu schreiben?!?!
Gibts da vielliecht Literatur zu, ich habe das noch nie gesehn und finde auch nichts.

2.) WIeso is das 	private static final int[] BALLS;
privat, statisch und final???

3.) 

```
private static boolean pruefeZahl(int zahl, int[] balls) {
        Arrays.sort(balls);
        int index = Arrays.binarySearch(balls, zahl);
        if(zahl < 0 || index < 0) {
            return false;
        }
        balls[index] = -1;
        return true;
    }
```
Also wieso jetzt true und false returnt wird ist klar.
Aber wieso sind in der Methode int zahl und int [] balls als Paramter angegeben??
Wieso werden die Arrays vorher sortiert.
Was ist der Sinn von Binary Search und wieso 

        if(zahl < 0 || index < 0) ???

Sorry aber vielleicht haste ja kurz Zeit mir zu erklren was du damit meintest


----------



## recess (1. Nov 2012)

> Zitat: hüteüberhüte
> 
> Beitrag anzeigen
> 
> ...



Der letzte Teil schmeckt mir irg wie nicht 
WIeso muss die Main throw IOE.....???

In meiner Main steht so viel zeug drin, wie und wo und warum soll ich  

System.out.println(Arrays.toString(lottozahlenEinlesen())); 

noch mit einbinden?


Außerdem find ich deinen Vorschlag mit hashset und Set noch verwirrender, weil ich davon noch nie etwas gehört habe :lol: ABer der Code sieht ansonstne einfacher aus das stimmt :toll:


----------



## KuhTee (1. Nov 2012)

recess hat gesagt.:


> Sorry , aber ich verstehe immernoch bahnhof.


Das ist nicht schlimm bei diesem Code 
Wenn ich jetzt gehässig wäre, würde ich sagen: vergiss diesen Code und machs bloß nicht so. Bei einer Firma mit Codereview würde dieser Code wegen unnötiger Umständlichkeit durchfallen.



recess hat gesagt.:


> WIeso muss die Main throw IOE.....???


Weil hüteüberhüte der Meinung ist, dass die Anwendung bei einer IOException beendet werden sollte.



recess hat gesagt.:


> In meiner Main steht so viel zeug drin, wie und wo und warum soll ich
> 
> System.out.println(Arrays.toString(lottozahlenEinlesen()));
> 
> noch mit einbinden?


Das ist ja nur zur Ausgabe, um die endgültigen Zahlen zu sehen. Ersetz es durch Code, den du brauchst.



recess hat gesagt.:


> Außerdem find ich deinen Vorschlag mit hashset und Set noch verwirrender, weil ich davon noch nie etwas gehört habe :lol:


Ein Set ist schlichtweg eine Collection, welche jeden Wert nur ein einziges Mal aufnehmen kann. Bietet sich also für sowas wie Lottozahlen an.



recess hat gesagt.:


> ABer der Code sieht ansonstne einfacher aus das stimmt :toll:


Das ist die jahrelange Erfahrung mit Lotto :toll:


----------



## recess (1. Nov 2012)

Allerdings meckert der Compiler bei 

"	    return Arrays.sort(result.toArray());"


----------



## hüteüberhüte (1. Nov 2012)

@KuhTee: Meine erste Lösung hatte 23 Zeilen. Deine umfasst schon 25 Zeilen. Was soll daran also kürzer sein?

Für mich ist immer noch die Erste am Besten geeignet. Wenn du das anders siehst, ist dies deine Meinung, die ich respektiere, und nicht weiter darauf eingehen möchte.

Hier ist auch nochmal eine andere Variante:

```
private static int[] lottozahlenEinlesen() throws IOException {
        int[] result = new int[6];
        boolean[] contai = new boolean[50];
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        for (int i = 0; i < result.length;) {
            System.out.print(i + 1 + ". Zahl eingeben: ");
            int input = pruefeLine(reader.readLine(), contai);
            if (input == -1) {
                System.out.println("Bitte Zahl zwischen 1 und 49 eingeben.");
            } else {
                result[i++] = input;
            }
        }
        Arrays.sort(result);
        return result;
    }

    private static int pruefeLine(String s, boolean[] a) {
        int result;
        try {
            result = Integer.parseInt(s);
        } catch (NumberFormatException numberFormatException) {
            return -1;
        }
        if (result < 1 || result > 49 || a[result]) {
            return -1;
        }
        a[result] = true;
        return result;
    }
```

@recess: Nimm irgendeine der hier vorgeschlagenen Lösungen und (btw) drücke nebenbei noch auf Danke.


----------



## recess (1. Nov 2012)

ok super, aber vielleicht möchtest du mir auch noch auf meine Fragen antworten? Also bisher funktionieren ehrlich gesagt nur deine beiden vorschläge hüte 

Will damit aber KuhTee nicht schlecht machen, es klappt nur irg wie bei mir nicht..

@hütte: Vielleicht möchtest du mir noch auf meine fragen antworten, damit ich falls ich deinen ersten Vorschlag nehme auch weiß was ich da gemacht habe


----------



## Spacerat (1. Nov 2012)

@recess: Vllt. hilft zum Thema static ja dieses hier schon weiter.

http://www.java-forum.org/stichwort-static/1353-bedeutet-static.html
Galileo Computing :: Java ist auch eine Insel – 5.6 Klassen- und Objektinitialisierung *

Das [c]if(zahl < 0 || index < 0)[/c] sollte eigentlich [c]if(zahl < 1 || index < 0)[/c] heissen.
"Arrays.binarySearch()" sucht in einem Array einen übergebenen Wert und gibt entweder dessen Index (wenn der Wert gefunden wurde) oder negative Werte (wenn der Wert nicht gefunden wurde) zurück. Das Array muss für eine Binärsuche zuvor sortiert werden.
Im vorliegenden Fall kann der höchste Wert, der gefunden werden kann nur 49 und der kleinste nur 1 sein, so wird das anfangs durch den klassenweiten Kugelsatz festgelegt. Das Array, welches aber an "pruefeZahl()" übergeben wird, ist jedoch nicht der klassenweite Kugelsatz, sondern die für den aktuellen Tipp verwendete Kopie davon. Aus dieser Kopie dürfen getippte Kugeln entnommen werden, das geschieht mit [c]balls[index] = -1[/c]. Da "-1" unterhalb des gültigen Zahlenbereichs 1 - 49 liegt, können diese durch "Arrays.binarySearch()" nicht mehr gefunden werden.

privat static final ist der Kugelsatz, weil er so in jeder Instanz der Klasse verfügbar, aber nicht nach aussen (in anderen Klassen) sichtbar ist.

[EDIT]...und für jene, die es evtl. doch mal vernünftig mit Sets probieren wollen, oder jenen den Arrays.binarySearch nicht liegt:

```
import java.util.Collections;
import java.util.InputMismatchException;
import java.util.Scanner;
import java.util.SortedSet;
import java.util.TreeSet;

public class Lotto {
	private static final SortedSet<Integer> BALL_SET;

	static {
		SortedSet<Integer> ballSet = new TreeSet<>();
		for(int n = 0; n < 49; n++) {
			ballSet.add(n + 1);
		}
		BALL_SET = Collections.unmodifiableSortedSet(ballSet);
	}

	public int[] tipp() {
		SortedSet<Integer> balls = new TreeSet<>(BALL_SET);
		int[] tipp = new int[6];
		Scanner sc = new Scanner(System.in);
		int zahl = 0;
		for(int n = 0; n < 6; n++) {
			try {
				zahl = sc.nextInt();
			} catch (InputMismatchException e) {
				sc.next();
				System.out.println("ZAHLEN! Keine Buchstaben! Nochmal");
				n--;
				continue;
			}
			if(!pruefeZahl(zahl, balls)) {
				System.out.println("Zahl ungueltig oder schon getippt. Nochmal");
				n--;
			} else {
				tipp[n] = zahl;
			}
		}
		sc.close(); // Also dieses close hier ist echt eklig!
		return tipp;
	}

	private static boolean pruefeZahl(int zahl, SortedSet<Integer> balls) {
		if(balls.contains(zahl)) {
			balls.remove(zahl);
			return true;
		}
		return false;
	}
}
```
[/EDIT]


----------



## KuhTee (1. Nov 2012)

recess hat gesagt.:


> Allerdings meckert der Compiler bei
> 
> "	    return Arrays.sort(result.toArray());"


Stimmt. Sort funktioniert ja so auch nicht  So etwa sollte es gehen:

```
Integer[] bla = result.toArray();
Arrays.sort(bla);
usw...
```



Spacerat hat gesagt.:


> ...und für jene, die es evtl. doch mal vernünftig mit Sets probieren wollen, oder jenen den Arrays.binarySearch nicht liegt:


Wofür ich das Set verwenden würde, hast du noch nicht so ganz verstanden, oder?  Deine ganze "BALLS" Geschichte ist unnütz. Pack die "tipps" doch einfach ins Set. So schwer ist das doch nicht. Egal...

Aber wenn wir schon dabei sind... Hierfür:

```
n--;
continue;
```
sollte dich irgendjemand ganz dolle schlagen.


----------



## recess (1. Nov 2012)

```
private  int [] lottozahlenEinlesen() throws IOException {
        int[] tippabgabe = new int[6];
        boolean[] prüfen = new boolean[50];
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        for (int i = 0; i < tippabgabe.length;) {
            System.out.print(i + 1 + ". Zahl eingeben: ");
            int einlese = pruefeLine(reader.readLine(), prüfen);
            if (einlese == -1) {
                System.out.println("Bitte Zahl zwischen 1 und 49 eingeben.");
            } else {
                tippabgabe[i++] = einlese;
            }
        }
        Arrays.sort(tippabgabe);
        objektTippSpeicher.speichere(new Tipp(tippabgabe));
		return tippabgabe;
    }
 
    private static int pruefeLine(String s, boolean[] pruefen) {
        int tippabgabe;
        try {
            tippabgabe = Integer.parseInt(s);
        } catch (NumberFormatException numberFormatException) {
            return -1;
        }
        if (tippabgabe < 1 || tippabgabe > 49 || pruefen[tippabgabe]) {
            return -1;
        }
        pruefen[tippabgabe] = true;
        return tippabgabe;
    }[code=Java]



eine Frage noch hierzu, wo wird denn pruefen überprüft?? In der untern Methode wird pruefen ja auf true gesetzt, wofür??
Also es funktioniert, ich sehe aber nirgendwo das auf diese bedingung überprüft wird??!!
```


----------



## hüteüberhüte (1. Nov 2012)

pruefeLine prüft, ob sich der übergebene String in ein int umwandeln lässt Und zwischen 1 und 49 liegt Und nicht bereits in dem boolean-Array vorhanden ist. Ist dies der Fall, wird der entsprechende Eintrag des boolean[] auf true gesetzt und die Zahl (int) zurückgegeben. Andernfalls wird -1 zurückgegeben.

In lottozahlenEinlesen wird dann geprüft, ob die eingelesene Zeile valide ist (Wert zwischen 1 und 49) oder nicht (-1). Wenn ja, wird sie als nächstes Element tippabgabe hinzugefügt.

Eine IOException kann natürlich auftreten, wenn der Benutzer z.B. die Eingabe schließt. Deshalb muss diese auch behandelt werden. In diesem Fall wird sie einfach an die aufrufende Methode weitergegeben.

Welche Zeilen sind für dich noch unklar?

int-Array wird deklariert und initialisiert...
boolean-Array wird deklariert und initialisiert...
BufferedReader wird deklariert und initialisiert (dem Konstruktor wird eine neues InputStreamReader-Objekt mitgegeben usw.)...
for-Schleife mit Initialisierung, Überprüfung und (leerer) Inkrementierungs-Anweisung (sie läuft von 0 bis (einschließlich) 6)...
Ausgabe auf der Konsole (der Ausdruck innerhalb des Methodenaufrufs wird von links nach rechts ausgewertet: zuerst int-Addition, dann String-Konkatenation)...
Deklaration und Zuweisung des Ergebnis des Aufrufs von pruefeLine an input...
Fallunterscheidung (bedingte Anweisung), wie oben bereits erwähnt...
usw.

Der Rest müsste verständlich sein!

Hth!


----------



## Spacerat (1. Nov 2012)

hüteüberhüte hat gesagt.:


> Eine IOException kann natürlich auftreten, wenn der Benutzer z.B. die Eingabe schließt. Deshalb muss diese auch behandelt werden.


Schreib' ruhig "D**p", denn was anderes fällt mir für meinen "Fauxpas" auch nicht ein... obwohl, ist ja nicht mein Fehler, wenn Oracle oder Eclipse der Meinung ist, da müsste was geschlossen werden... dann bin ich halt der "D**p", der das auch noch macht. Das Schlimme daran... "System.in" wird tatsächlich auch noch geschlossen... Das kann von denen, die da plötzlich eine offen gelassene Ressource anprangern kaum so gewollt sein. Wie man's ohne Warnungen und Exceptions dennoch hinbekommt (anonymer InputStream delegiert alles bis auf "close()" an System.in), sprengt allerdings das Thema dieses Threads und ist Umstand hoch drei.


----------



## Guest2 (2. Nov 2012)

Moin,

sowohl der Reader als auch der Scanner werfen zwangsweise eine Exception, sofern die Eingabe ungültig ist. Imho ist es legitim sich dort mit einer weiteren Exception dran zu hängen, um die Eingabe auf ein gültiges Intervall einzugrenzen. Die Exceptions zu fangen und durch ein "Magic-Value" (-1, 0, 1337) zu ersetzen halte ich hingegen für ungünstig.

Auch finde ich es relativ unerheblich ob die Methode 23 oder 25 Zeilen lang ist. Lieber mehrere kurze knackige Methoden als eine lange, auch wenn es insgesamt mehr Zeilen sind. (In dem Beispiel unten sogar wesentlich mehr )

Spontan würde ich das etwa so implementieren:

lottery.BallValue:

```
package lottery;


public final class BallValue implements Comparable<BallValue> {

    private static final int LOWEST_VALUE  = 1;
    private static final int HIGHEST_VALUE = 49;


    private final int        value;


    public BallValue(final int value) {

        if (value < LOWEST_VALUE || value > HIGHEST_VALUE)
            throw new IllegalArgumentException();

        this.value = value;

    }


    public int getValue() {

        return value;

    }


    @Override
    public int compareTo(final BallValue o) {

        if (value < o.value)
            return -1;

        else if (value == o.value)
            return 0;

        else
            return 1;

    }


    @Override
    public String toString() {

        return String.valueOf(value);

    }


}
```

lottery.Lotto:

```
package lottery;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Set;
import java.util.TreeSet;


public final class Lotto {

    private static final int     NUMBER_OF_PICKS     = 6;

    private final BufferedReader readerForUserInput  = new BufferedReader(new InputStreamReader(System.in));
    private final Set<BallValue> pickedBallsFromUser = new TreeSet<>();


    public void printPickedBalls() {

        System.out.print("Folgende Zahlen wurden gewählt: " + pickedBallsFromUser);

    }


    public void letUserPickAllBallValues() {

        while (pickedBallsFromUser.size() < NUMBER_OF_PICKS)
            letUserPickOneSingeBallValue();

    }


    private void letUserPickOneSingeBallValue() {

        System.out.print((pickedBallsFromUser.size() + 1) + ". Zahl eingeben: ");

        try {

            final BallValue ballValue = readBallValueFromUser();

            if (pickedBallsFromUser.contains(ballValue))
                System.out.println("Zahl bereits gewählt!");

            else
                pickedBallsFromUser.add(ballValue);

        } catch (IllegalArgumentException | IOException e) {

            System.out.println("Eingegebene Zahl war ungültig!");

        }

    }


    private BallValue readBallValueFromUser() throws IOException {

        final String input = readerForUserInput.readLine();
        final int integer = Integer.parseInt(input);
        final BallValue ballValue = new BallValue(integer);

        return ballValue;

    }


    public static void main(final String[] args) {

        final Lotto lotto = new Lotto();

        lotto.letUserPickAllBallValues();
        lotto.printPickedBalls();

    }

}
```

Viele Grüße,
Fancy


----------



## hüteüberhüte (2. Nov 2012)

Guest2 hat gesagt.:


> Die Exceptions zu fangen und durch ein "Magic-Value" (-1, 0, 1337) zu ersetzen halte ich hingegen für ungünstig.



Kennst du eig. die Methode indexOf (oder zahlreiche andere Methoden)?

Jetzt extra Klassen einzuführen, und damit den Code unnötig aufzublähen, halte ich hingegen für die schlechteste Lösung. 6, setzen, bitte... (Auch wenn viele andere aus welchen Gründen auch immer deine Beiträge sehr zu schätzen wissen...)


----------



## Guest2 (2. Nov 2012)

hüteüberhüte hat gesagt.:


> Kennst du eig. die Methode indexOf (oder zahlreiche andere Methoden)?



Natürlich. Allerdings ist zu erwarten, dass -1 niemals ein gültiger Index innerhalb eines Strings sein wird. Für eine Methode die eine Zahl vom Nutzer einliest, kann -1 aber durchaus mal ein gültiger Wert sein.




hüteüberhüte hat gesagt.:


> Jetzt extra Klassen einzuführen, und damit den Code unnötig aufzublähen, halte ich hingegen für die schlechteste Lösung. 6, setzen, bitte...



Bei meinem Ansatz gibt es exakt eine Klasse, in der das gültige Intervall festgelegt wird. In allen anderen Klassen ist das Intervall dann vollkommen unerheblich. Auch ist mein Ansatz nicht aufgebläht, sondern die SLOC/Methode sind sogar deutlich reduziert. Es gibt hier im Forum einige die 3 - 5 SLOC pro Methode als geeignet ansehen.

Aber ich habe mit Deiner Reaktion gerechnet. 

Viele Grüße,
Fancy


----------



## hüteüberhüte (2. Nov 2012)

Rechne mit was du willst.

Bin mal gespannt drauf, ob ihr Oberschlaumeier folgendes kürzer _und_ übersichtlicher hinbekommen würdet:

```
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * @author hüte
 */
public class Lotto {

    private static final BufferedReader reader =
            new BufferedReader(new InputStreamReader(System.in));
    private static final Random random =
            new Random();
    private static final List<int[]> ziehungen =
            new ArrayList<int[]>(1000);
    private static final List<int[]> tipps =
            new ArrayList<int[]>();

    public static void main(String[] args) throws IOException {
        a:
        for (;;) {
            System.out.println("Willkommen in Ihrem Lottoprogramm");
            System.out.println("---------------------------------");
            System.out.println("(1) Ich will n-Ziehungen automatisch (per Zufall) generieren lassen, dabei n selber vorgeben!");
            System.out.println("(2) Ich will 1000 Ziehungen generieren und abspeichern lassen!");
            System.out.println("(3) Ich will alle Ziehungen (Lottozahlen) ausgeben!");
            System.out.println("(4) Ich möchte einen Tipp abgeben!");
            System.out.println("(5) Ich will n-Tipps automatisch (per Zufall) generieren lassen, dabei n selber vorgeben!");
            System.out.println("(6) Ich möchte alle Tipps ausgeben!");
            System.out.println("(7) Ich will das Programm beenden!");
            switch (readInt(1, 7)) {
                case 1:
                    one();
                    break;
                case 2:
                    two();
                    break;
                case 3:
                    three();
                    break;
                case 4:
                    four();
                    break;
                case 5:
                    five();
                    break;
                case 6:
                    six();
                    break;
                case 7:
                    break a;
            }
        }
    }

    private static void one() throws IOException {
        System.out.println("n eingeben!");
        int n = readInt(1, Integer.MAX_VALUE);
        for (int i = 0; i < n; i++) {
            ziehungen.add(generiereZiehung());
        }
    }

    private static void two() {
        for (int i = 0; i < 1000; i++) {
            ziehungen.add(generiereZiehung());
        }
    }

    private static void three() {
        for (int i = 0; i < ziehungen.size(); i++) {
            System.out.println(i + 1
                    + ". Ziehung: "
                    + Arrays.toString(ziehungen.get(i)));
        }
    }

    private static void four() throws IOException {
        int[] tipp = new int[6];
        for (int i = 0; i < tipp.length;) {
            System.out.println(i + 1 + ". Zahl eingeben!");
            int t = readInt(1, 49);
            if (t != -1 && Arrays.binarySearch(tipp, t) < 0) {
                tipp[0] = t;
                Arrays.sort(tipp);
                i++;
            }
        }
        tipps.add(tipp);
    }

    private static void five() throws IOException {
        System.out.println("n eingeben!");
        int n = readInt(1, Integer.MAX_VALUE);
        for (int i = 0; i < n; i++) {
            tipps.add(generiereZiehung());
        }
    }

    private static void six() {
        for (int i = 0; i < tipps.size(); i++) {
            System.out.println(i + 1
                    + ". Tipp: "
                    + Arrays.toString(tipps.get(i)));
        }
    }

    private static int readInt(int from, int to) throws IOException {
        try {
            int i = Integer.parseInt(reader.readLine());
            if (i >= from && i <= to) {
                return i;
            }
        } catch (NumberFormatException numberFormatException) {
        }
        return -1;
    }

    private static int[] generiereZiehung() {
        int[] result = new int[6];
        boolean[] contains = new boolean[50];
        for (int i = 0; i < result.length;) {
            int r = random.nextInt(49) + 1; // 1..49
            if (!contains[r]) {
                contains[r] = true;
                result[i++] = r;
            }
        }
        Arrays.sort(result);
        return result;
    }
}
```

136 Zeilen...

Hth!

PS: Über ein Dank würd ich mich sehr freuen.


----------



## Spacerat (2. Nov 2012)

@hüte:
1. Kein Mensch würde so Programmieren, Übersicht geht anders, wenn auch zu Lasten der Zeilenanzahl, auf die es aber nicht ankommt. Bin mir fast sicher, du weist nicht mal, was SLOCs sind. Kleiner Tipp, man trägt sie keineswegs an den Füßen.
2. Leute, die sich selbst als Nonplusultra der Entwicklergilde betrachten, gehören eigentlich auf die Ignorierliste. Ist ja keineswegs so, dass sie nicht auch mal Hilfe benötigen, wofür sie sich ebenso wenig bedanken würden, sogar dezente Tipps schlagen sie aus und verlangen stattdessen, dass man sich für ihre permanente Überheblichkeit auch noch bei ihnen bedankt. Dabei vergessen sie stets, dass immer einer besser kann.
3. Bist du ADHS-Patient? (ADHS -> Aufmerksamkeitsdefizit-/Hyperaktivitätsstörung) In dem Falle nehm' ich sogar Rücksicht.


----------



## hüteüberhüte (2. Nov 2012)

Beitrag gemeldet. Eine kleine Sperre würde dir (und vor allem uns) glaube ich mal ganz gut tun.


----------



## Gast2 (2. Nov 2012)

Hrm... selber austeilen und sich dann beschweren... *kopschüttel*

Ich kann da nur zustimmen, du bist uneinsichtig und völlig beratungsresistent - auch wenn die Tips von erfahrenen Entwicklern kommen und wirklich sinnvolle Denkanstöße bieten.


----------



## hüteüberhüte (2. Nov 2012)

Naja, Erfahrung heißt eben nicht alles, solange nur Vorschläge kommen, die eher zu einer Verschlechterung als Verbesserung führen (z.B. bei der Lesbarkeit oder Performance, siehe Spacerat s letzten Versuch). Ich bleib dabei, solange jemand nichts besseres vorweisen kann, betrachte ich seine Beiträge als Spam oder Trolling. Mir ist es auch wurscht, wie beliebt oder hoch angesehen Fancy hier bei einigen anscheinend ist.

@Spacerat: 1. Du würdest vielleicht nicht so programmieren, weil du es nicht besser _kannst_. 2. Wenn man gezeigt bekommt, wies richtig gemacht wird, denke ich, sollte ein dezentes Danke schon drin sein. Wie wär's, wenn du auch mal über deinen eigenen Schatten springen würdest und dich bedankst? 
3. Wie gesagt, bereits gemeldet.


----------



## Jango (2. Nov 2012)

Macht weiter, ich amüsiere mich köstlich!

Der TO hat schon längst das Interesse an dem Thread verloren, aber es wird geflamt ohne Ende.
Und wer ist nicht nur wieder mittendrin, sondern auch der Hauptflamer? hüteüberhüte... :lol:


----------



## KuhTee (2. Nov 2012)

Guest2 hat gesagt.:


> sowohl der Reader als auch der Scanner werfen zwangsweise eine Exception, sofern die Eingabe ungültig ist. Imho ist es legitim sich dort mit einer weiteren Exception dran zu hängen, um die Eingabe auf ein gültiges Intervall einzugrenzen.


Damit vermengt man aber kritische Fehlerbehandlung mit Programmlogik. Finde ich nicht so prickelnd.



Guest2 hat gesagt.:


> Die Exceptions zu fangen und durch ein "Magic-Value" (-1, 0, 1337) zu ersetzen halte ich hingegen für ungünstig.


Doch, das ist sogar ziemlich günstig und praktisch in den meisten Fällen. Ich zB verwende ich ca 80% meiner Projekte eine Methode "toInt(String s, int onError)". In den meisten Fällen ist es so, dass das Programm nicht einfach mit einer Fehlermeldung abschmieren darf, sondern weiterlaufen muss, und zwar mit einem Default Wert. Und das ist dann auch kein "Magic value", sondern ein ganz Problembezogener Wert. So wie auch in diesem Fall. Daher interpretiert man bei den Lottozahlen eine Nichtzahl einfach als 0.

Dein Code ist übersichtlich und lesbar, aber verwendest auch wieder Exceptions zur Programmflusssteuerung. Die Dinger heissen "Ausnahmen". Unter einer Ausnahme sollte man eher etwas verstehen wie Division durch 0, Zugriff auf nicht existierenden Arrayindex etc. Aber reine Programmlogik? Neeee... Aber das können wir ja anderswo diskutieren


----------



## KSG9|sebastian (2. Nov 2012)

Also huette, vielleicht solltest du die Füße ein wenig still halten. Deine Codeneispiele entsprechen eher dem Paradigma "Masse statt Klasse", vielleicht solltest du akzeptieren was die erfahrenen Leute hier posten!
Die Anzahl an Zeilen hat mal nix mit Performance zu tun, Code der von oben nach unten durchläuft ist nicht lesbar sondern eher Spaghetticode, daher lass es mal gut sein.

Auch wenn der Artikel vielleicht zu viel des Guten ist beschreibt er soziemlich jeden "Bock" in deinem Code:
One Jar To Rule Them All: Avoiding FORs - Anti-If Campaign

Für jede Zahl ne eigene Methode zu machen..sorry.. epic fail!

** Edith sagt **
Bei ner IOException das Programm beenden? Nicht dein Ernst? Wie kommst du auf solche "Patterns"?


----------



## Guest2 (2. Nov 2012)

KuhTee hat gesagt.:


> Doch, das ist sogar ziemlich günstig und praktisch in den meisten Fällen. Ich zB verwende ich ca 80% meiner Projekte eine Methode "toInt(String s, int onError)". In den meisten Fällen ist es so, dass das Programm nicht einfach mit einer Fehlermeldung abschmieren darf, sondern weiterlaufen muss, und zwar mit einem Default Wert.



Weiterlaufen mit einem Default-Wert wäre für mich im Allgemeinen in Ordnung. Hier ist dies jedoch nicht so, das Verhalten der anderen Methoden unterscheidet sich zwischen -1 und anderen Werten. Die -1 ist nach meinem Empfinden also klar ein Magic-Value.

Das Verhalten der Java API Klassen/Methoden ist öffentlich dokumentiert und bekannt. Kapselt man eine dieser Methoden und verändert ihr Verhalten, also z.B. das Fangen einer Exception und umwandeln in ein Magic-Value, dann verändert man ein standardkonformes Verhalten in ein nicht standardkonformes Verhalten.

Unter C verwende ich diese Art der Rückgabe ebenfalls. Jedoch ist dies dort Usus. Hier in Java werfen die entsprechenden Methoden der API Exceptions und die Aufgabe des Entwicklers ist es imho, sinnvoll darauf zu reagieren. Das Umwandeln in ein Magic-Value ist für mich keine sinnvolle Reaktion.

Möchte man z.B unbedingt das Verhalten von Integer.parseInt() anpassen und die NumberFormatException kapseln, würde ich ehr etwas in dieser Art vorschlagen:


```
public final class CustomUserInput {

    private final BufferedReader reader;

    private Integer              lastValidValue;


    public CustomUserInput(final BufferedReader reader) {

        this.reader = reader;

    }


    public boolean readNextInt() throws IOException {

        try {

            final String input = reader.readLine();
            final int integer = Integer.parseInt(input);
            lastValidValue = integer;

            return true;

        } catch (final NumberFormatException e) {

            return false;

        }

    }


    public Integer getLastValidValue() {

        return lastValidValue;

    }

}
```

(Das geht dann in die Richtung von Scanner.hasNextInt() / nextInt())

Viele Grüße,
Fancy


----------



## hüteüberhüte (2. Nov 2012)

@maki: Ist es ok für Dich, Personen ADHS zu unterstellen? Für mich jedenfalls nicht. Das gehört nicht zum Thema, das gehört nicht hierher



KSG9|sebastian hat gesagt.:


> Die Anzahl an Zeilen hat mal nix mit Performance zu tun



Na, das ist ja mal eine ganz neue Erkenntnis... [/IRONIE] Hab ich auch nicht behauptet. Viel besser (schneller) als das Ziehen der Lottokugeln oben kann es aber eig. gar nicht gehen...



KSG9|sebastian hat gesagt.:


> Code der von oben nach unten durchläuft ist nicht lesbar sondern eher Spaghetticode, daher lass es mal gut sein.



Code, der also zwischen Methoden immer hin und her springt, oder Programmlogik ausschließlich in der main-Methode ist also deiner Meinung nach kein "Spaghetticode"? Imho bist du dir über den Begriff noch nicht ganz im Klaren.

Bei einer Auswahl von 1 bis 7 wird man um eine Fallunterscheidung gar nicht drumrum kommen...



KSG9|sebastian hat gesagt.:


> Für jede Zahl ne eigene Methode zu machen..sorry.. epic fail!



Überleg doch mal, was du da schreibst *kopfschüttel*. Andernfalls würde es bedeuten, du schreibst alles in eine Methode. Dann wirds erst recht unübersichltich...

@all: Afaics hat von euch bis jetzt noch niemand einen besseren Vorschlag gemacht... (außer einer Klasse für das Einlesen, die aber auch darauf beruht, im Ausnahmefall ein Default-Value zurückzugeben...)

Würde mich freuen, wenn der TO zurückkehrt und auch noch etwas dazu schreibt, was er nehmen würde.


----------



## maki (2. Nov 2012)

> @maki: Ist es ok für Dich, Personen ADHS zu unterstellen? Für mich jedenfalls nicht. Das gehört nicht zum Thema, das gehört nicht hierher


Habe mich selber schon gefragt was mit dir nicht stimmt...

Es steht dir nicht zu Leuten Sperren anzudrohen PUNKT.
Da entscheidest du nicht im geringsten, empfehle dir dringend mehr Zurückhaltung.
Beim nächsten mal hast du wieder eine Sperre.

Ansonsten solltest du dir nicht mal anmassen überhaupt Code zu bewerten, da du offensichtlich von der Materie (SW Entwicklung, Java, sauberer Code, etc. pp.) keine Ahnung hast.


----------



## hüteüberhüte (2. Nov 2012)

@maki: Naja, dann sperr mich doch gleich. Ich halte mich jedenfalls nicht zurück, wenn irgendjemand mir gegenüber beleidigend wird, und das vom Team einfach ignoriert wird. Dann ist es für mich auch egal, hier weiter angemeldet zu sein... Also dann zeig mal, was du für ein genialer Moderator bist.


----------



## ThreadPool (2. Nov 2012)

maki hat gesagt.:


> [...]
> Ansonsten solltest du dir nicht mal anmassen überhaupt Code zu bewerten, da du offensichtlich von der Materie (SW Entwicklung, Java, sauberer Code, etc. pp.) keine Ahnung hast.



Es steht dir frei besseren Code zu posten. Dann trägst du wenigstens was zur Diskussionsgrundlage bei. Latente Beleidigungen gehören nicht dazu. Und klar hier im Forum herrscht keine Demokratie aber genausowenig wünsche ich mir eine makitatur.

Davon abgesehen finde ich das Thema interessant, da nur sehr wenige Code gepostet haben der sich teilweise auch recht ähnlich ist, bis auf Fancy dessen Teillösung man schon wieder fast in die Ecke "over-engineering" stopfen könnten. Ich warte gespannt auf weitere gehaltvollere Beiträge.


----------



## Spacerat (2. Nov 2012)

@Hüte: Das war keine Unterstellung, sondern schlicht 'ne Frage und diese war durchaus ernst gemeint. Aber egal, von nun an bekomme ich deine Beiträge ohnehin nur noch in Zitaten anderer mit, sofern es welche gibt. Mir erspart das jede Menge unnütze Flames und dir möglicherweise die Sperrung. Evtl. beherzigst du das mal, wenn es sich erneut ergibt, dass ein Schuss nach hinten los geht.


----------



## maki (2. Nov 2012)

ThreadPool hat gesagt.:


> Es steht dir frei besseren Code zu posten. Dann trägst du wenigstens was zur Diskussionsgrundlage bei. Latente Beleidigungen gehören nicht dazu. Und klar hier im Forum herrscht keine Demokratie aber genausowenig wünsche ich mir eine makitatur.


Dir steht es frei deine Meinung zu äussern, diese muss aber nicht notwendigerweise Sinn machen.. 

Deine einzige bisherige Meinungsäusserung in diesem Thread kam anscheinend erst durch meinen Post(?), also erspar mir deine "trägt zur Diskussionsgrundlage bei" gerede wenn du das nicht ernst meinst.

Grund warum ich  hier gepostet habe war dass gleich mehrere Meldungen von hth bezüglich einer angeblichen Beleidung kamen, zus. droht er anderen Usern mit sperren und  dann bewertet er auch noch..

Dir muss nicht gefalen was ich zu sagen habe, kannst du das gerne "makitatur" oder sonstwie nennen, Tatsache ist aber dass deine Meinung zu bestimmten Themen irrelevant ist.


----------



## ThreadPool (2. Nov 2012)

maki hat gesagt.:


> [...]also erspar mir deine "trägt zur Diskussionsgrundlage bei" gerede wenn du das nicht ernst meinst.[...]



Maki, Kritik üben ist einfach, kritikfähig sein nicht. Und natürlich meinte ich das in Bezug auf die zitierte Passage von dir ernst. Du übst fachliche Kritik ohne konstruktive, anderslautende Vorschläge, wie so viele Andere auch (scheint heutzutage Gewohnheit zu sein). Aber Man lernt ja nur von Besseren, und wir wollen hier alle lernen (zumindest ich). Und du hast dich in deiner Aussage über jmden gestellt und ich
frage nun dich nach einer besseren Lösung.


----------



## schalentier (2. Nov 2012)

Koennt ihr das bitte direkt per PM klaeren oder woanders. Das stoert hier doch nur.

Weiss gar nich was das Problem ist, der Code huete ist doch nicht schlecht. Einige Anmerkungen dazu hab ich aber: 

- Ich wuerde die Methoden nicht durchnummerieren, sondern sinnvolle Namen geben.
- Ich wuerde die IOException schnell fangen (in der readInt) und in eine RuntimeException gewrappt weiterwerfen ([c]...catch(IOException e) { throw new RuntimeException(e); }[/c]). 
- Mach das komische Label weg. Einfach wegmachen und direkt vergessen. Imho is das dort sogar nen Bug, weil man das Programm so nicht beenden kann... ;-)


----------



## Spacerat (2. Nov 2012)

ThreadPool hat gesagt.:


> Maki, Kritik üben ist einfach, kritikfähig sein nicht. Und natürlich meinte ich das in Bezug auf die zitierte Passage von dir ernst. Du übst fachliche Kritik ohne konstruktive, anderslautende Vorschläge, wie so viele Andere auch (scheint heutzutage Gewohnheit zu sein). Aber Man lernt ja nur von Besseren, und wir wollen hier alle lernen (zumindest ich). Und du hast dich in deiner Aussage über jmden gestellt und ich frage nun dich nach einer besseren Lösung.


Jetzt kriegt ihr euch nicht auch noch in die Haare... ist ja nicht auszuhalten.

Ich bin zwar nicht gefragt, aber wenn es so wär' kann ich dir zumindest einen Grund nennen, warum "konstruktive, anderslautende Vorschläge" des öfteren von dem ein oder anderen ausbleiben, nämlich wenn die Lösung schlicht "ändere dies!" lautet. Muss dass etwa immer betont werden?


----------



## Noctarius (2. Nov 2012)

schalentier hat gesagt.:


> - Mach das komische Label weg. Einfach wegmachen und direkt vergessen. Imho is das dort sogar nen Bug, weil man das Programm so nicht beenden kann... ;-)



Das war auch das Erste was ich heute morgen gesehen habe, bevor ich mit gutem Gewissen die Meldung ignorierte


----------



## hüte (2. Nov 2012)

Dann sollte man vllt. anstatt des Labels eine boolean-Variable einführen. Mir ist bekannt dass Sprungmarken evil sind und Dijkstra sie ablehnte (Go To Statement Considered Harmful), aber in diesem Fall würde das mMn den Code unnötig aufblähen. Aber vielen Dank für ernst gemeinten Vorschläge / konstruktive Kritik. Ich denke ich mache einfach mal ein paar Tage Pause, nachdem hier so ein Flame entstanden ist. Byebye


----------



## Guest2 (2. Nov 2012)

@Schalentier: Sein Code ist insofern nicht schlecht, als dass er vermutlich funktioniert (ich habe es nicht ausprobiert). Allerdings ist es auch ein rein prozeduraler Ansatz. Mit leicht anderer Syntax könnte sein Code genauso plain C oder Pascal sein.

Der TO zeigt in seinem Code jedoch das er zumindest erste objektorientierte Ansätze verfolgt. Und da Java weder C noch Pascal ist, halte ich es auch für sinnvoll diese Ansätze weiter auszubauen. Natürlich kann Clean Code und OOP schnell übertrieben wirken, gerade hier im Anfängerbereich muss das aber imho nicht schlecht sein. Es später hinhunzen, wenn die Zeit drängt, geht immer noch. (Mach ich auch )

Viele Grüße,
Fancy


----------



## schalentier (2. Nov 2012)

Also das vom TO ist doch fast genau der gleiche Code, nur dass dort extra Klassen fuer eine Liste von Tipps und eine Liste von Ziehungen erstellt wurden (was ich fuer ungut halte - zumindest hab ich soeben deutlich laenger gebraucht, bis ich das begriffen hab). Zu OOP gehoert auch Wiederverwenden von Klassen oder Sprachkonstrukten, nicht nur das Erzeugen moeglichst vieler Klassen und wilder Klassenhierarchien.

Eventuell koennte man tatsaechlich fuer Tipp und Ziehung Klassen einfuehren, find aber auch das hier eher uebertrieben. 

Am Ende haengt es aber von der Aufgabenstellung ab.


----------



## Noctarius (2. Nov 2012)

Wieso nicht einfach mit return aus der Methode springen wenn das Programm beendet werden soll?


----------

