Neuen Datentyp für rationale Zahlen als Klasse entwickeln

1207

Aktives Mitglied
Hallo 🙂
Ich soll einen neuen Datentyp für rationale Zahlen als Klasse entwickeln.
Zuerst soll ich die Klasse RationalNumber anlegen, dann ein privates Datenfeld vom Typ Integer anlegen und danach einen Konstruktor, der Zähler und Nenner der rationalen Zahlen als Parameter enthält:

public RationalNumber( int numerator, int denominator)

Die rationalen Zahlen sollen intern immer in gekürzter vorm dargestellt werden, deswegen soll ich eine Methode private void reduce() implementieren. Dafür habe ich eine Methode implementiert dieden ggT berechnet private static int greatestCommonDivisor(int a, int b).
Die Brüche sollen wann immer dies nötig gekürzt werden (auch im Konstruktor).

Ich soll auch eine Objektmethode public String getStringRepresentation() implementieren, die eine textuelle Repräsentation der rationalen Zahlen zurückgibt.

Bis dahin, glaube ich zumindest, habe ich das auch noch alles richtig gemacht

So jetzt soll ich eine Objektmethode public RationalNumber add(RationalNumber number) implementieren, die die rationale Zahl zur gegebenen rationalen Zahl number addiert und eine neue rationale Zahl entsteht.

Dann das gleiche nur mit der Subtraktion, also public RationalNumber sub(RationalNumber number).

Dann für die Multiplikation, also public RationalNumber mult(RationalNumber number).

Und dann auch noch für die Division, wo die rationale Zahl durch number geteilt wird, also public RationalNumber div(RationalNumber number).


Soweit ich das hinbekommen habe, habe ich das auch schon alles gemacht, also hier so :

Java:
class RationalNumber{
     
	private int numerator;
	private int denominator;
	
	public RationalNumber(int numerator, int denominator){
	     
		this.numerator = numerator;
		this.denominator = denominator;
		
		if (greatestCommonDivisor(numerator, denominator) != 1){
		     reduce();
		     }
	
	}
	
	public void ausgeben(){
	System.out.println( numerator + "/" + denominator);
	} 
	
	
	private static int greatestCommonDivisor(int a, int b) {
		while (a != b) {
			
			if (a > b)
				a = a - b;
			else
				b = b - a;
		}
		return a;
    }
	
	
	private void reduce(){
	
	    int a = greatestCommonDivisor(numerator, denominator);
	    numerator = numerator / a;
	    denominator = denominator / a;
	}
	
	
	public String getStringRepresentation(){
	
	    String string = ("" + numerator + "/" + denominator);
	    return string;
	}
	
	
	public void ausgeben3(){
	    System.out.println(getStringRepresentation());
	}
	
	public RationalNumber add(RationalNumber number){
	
	    int nenner = this.denominator * c.denominator;
	    int thisZaehler = this.numerator * c.denominator;
	    int cZaehler = c.numerator * this.denominator;
	    int zaehlerSum = thisZaehler + cZaehler;
		
		RationalNumber addZahl = new RationalNumber ( zaehlerSum, nenner );
	
	return addZahl;
	}
	
	public void ausgeben2() {
	    System.out.println( add( number ) );
	}
	
	
	public RationalNumber sub(RationalNumber number){          //ist wie add(...) nur statt + ein -
	
	    int nenner = this.denominator * c.denominator;
	    int thisZaehler = this.numerator * c.denominator;
	    int cZaehler = c.numerator * this.denominator;
	    int zaehlerSum = thisZaehler - cZaehler; 
        
        RationalNumber subZahl = new RationalNumber	( zaehlerSum, nenner );	
	
	return subZahl;
	}
	
	
	public RationalNumber mult(RationalNumber number) {
	
	    int nenner = this.denominator * c.denominator;
	    int zaehler = this.numerator * c.numerator;
	    RationalNumber multZahl = new RationalNumber( zaehler, nenner);
	
	return multZahl;
	}
	
	public RationalNumber div(RationalNumber number) {
	
	    int nenner = this.numerator * c.denominator;
		int zaehler = this.denominator * c.numerator;
		RationalNumber divZahl = new RationalNumber( zaehler, nenner );
	
	return divZahl;
	}
	
	
	
	public static void main(String[] args){
	
	    RationalNumber c = new RationalNumber(3, 12);
	    c.ausgeben(); 
	
	    c.reduce();
	    c.ausgeben();
	
	    c.getStringRepresentation();
	    c.ausgeben3();
	
	    RationalNumber r = new RationalNumber( 12, 6 );
	    r.reduce();
	    r.ausgeben2();
	
	
	}
}

Jetzt wird mir aber ab der Addition nichts mehr ausgegeben und ich bekommen diese Fehlermeldungen:

C:\Users\C_I_N_J_A\Desktop\Uni\PraktInfo▄bungsblõtter\Ubungsblatt6\RationalNumber.java:55: error: cannot find symbol
int nenner = this.denominator * c.denominator;
^
symbol: variable c
location: class RationalNumber
C:\Users\C_I_N_J_A\Desktop\Uni\PraktInfo▄bungsblõtter\Ubungsblatt6\RationalNumber.java:56: error: cannot find symbol
int thisZaehler = this.numerator * c.denominator;
^
symbol: variable c
location: class RationalNumber
C:\Users\C_I_N_J_A\Desktop\Uni\PraktInfo▄bungsblõtter\Ubungsblatt6\RationalNumber.java:57: error: cannot find symbol
int cZaehler = c.numerator * this.denominator;
^
symbol: variable c
location: class RationalNumber
C:\Users\C_I_N_J_A\Desktop\Uni\PraktInfo▄bungsblõtter\Ubungsblatt6\RationalNumber.java:66: error: cannot find symbol
System.out.println( add( number ) );
^
symbol: variable number
location: class RationalNumber
C:\Users\C_I_N_J_A\Desktop\Uni\PraktInfo▄bungsblõtter\Ubungsblatt6\RationalNumber.java:72: error: cannot find symbol
int nenner = this.denominator * c.denominator;
^
symbol: variable c
location: class RationalNumber
C:\Users\C_I_N_J_A\Desktop\Uni\PraktInfo▄bungsblõtter\Ubungsblatt6\RationalNumber.java:73: error: cannot find symbol
int thisZaehler = this.numerator * c.denominator;
^
symbol: variable c
location: class RationalNumber
C:\Users\C_I_N_J_A\Desktop\Uni\PraktInfo▄bungsblõtter\Ubungsblatt6\RationalNumber.java:74: error: cannot find symbol
int cZaehler = c.numerator * this.denominator;
^
symbol: variable c
location: class RationalNumber
C:\Users\C_I_N_J_A\Desktop\Uni\PraktInfo▄bungsblõtter\Ubungsblatt6\RationalNumber.java:85: error: cannot find symbol
int nenner = this.denominator * c.denominator;
^
symbol: variable c
location: class RationalNumber
C:\Users\C_I_N_J_A\Desktop\Uni\PraktInfo▄bungsblõtter\Ubungsblatt6\RationalNumber.java:86: error: cannot find symbol
int zaehler = this.numerator * c.numerator;
^
symbol: variable c
location: class RationalNumber
C:\Users\C_I_N_J_A\Desktop\Uni\PraktInfo▄bungsblõtter\Ubungsblatt6\RationalNumber.java:94: error: cannot find symbol
int nenner = this.numerator * c.denominator;
^
symbol: variable c
location: class RationalNumber
C:\Users\C_I_N_J_A\Desktop\Uni\PraktInfo▄bungsblõtter\Ubungsblatt6\RationalNumber.java:95: error: cannot find symbol
int zaehler = this.denominator * c.numerator;
^
symbol: variable c
location: class RationalNumber
11 errors


Also, dass er die Variable c nicht kennt, aber davor bei den Aufgaben erkennt er c doch noch?

Entschuldigung für die Länge und Textlastigkeit, ich habe schon versucht es so gut wie möglich zu kürzen!
 
Soweit ich das sehe, wird c nur in der main Methode deklariert. Da es dann eine lokale Variable ist, ist sie außerhalb der Methode nicht bekannt.
 
Dankeschön!
Aber wo muss ich c denn dann deklarieren, damit ich auch außerhalb der main-Methode darauf zugreifen kann?
Muss ich dann in den Methoden add, sub, mult, div c immer wieder neu deklarieren?
 
Ok, also ich glaube das mit dem c hab ich jetzt hinbekommen, aber meine Frage jetzt ist wie kann ich mir die neuen rationalen Zahlen die durch z.B. die Addition entstehen ausgeben lassen?

Das hier ist mein Versuch, aber offensichtlich ist der falsch...
Java:
class RatNumber{
     
	private int numerator;
	private int denominator;
	
	public RatNumber(int numerator, int denominator){
	     
		this.numerator = numerator;
		this.denominator = denominator;
		
		if (greatestCommonDivisor(numerator, denominator) != 1){
		     reduce();
		     }
	
	}
	
	public void ausgeben(){
	System.out.println( "Bruch: " + numerator + "/" + denominator);
	} 
	
	
	private static int greatestCommonDivisor(int a, int b) {
		while (a != b) {
			
			if (a > b)
				a = a - b;
			else
				b = b - a;
		}
		return a;
    }
	
	
	private void reduce(){
	
	    int a = greatestCommonDivisor(numerator, denominator);
	    numerator = numerator / a;
	    denominator = denominator / a;
	}
	
	
	public String getStringRepresentation(){
	
	    String string = ("" + numerator + "/" + denominator);
	    return string;
	}
	
	
	public void ausgeben3(){
	    System.out.println("Als Text: " + getStringRepresentation());
	}
	
	public RatNumber add(RatNumber number){
	
	    int nenner = this.denominator * denominator;
	    int thisZaehler = this.numerator * denominator;
	    int cZaehler = numerator * this.denominator;
	    int zaehlerSum = thisZaehler + cZaehler;
		
		RatNumber addZahl = new RatNumber ( zaehlerSum, nenner );
	
	return addZahl;
	}
	
	public void ausgeben2() {
	    System.out.println( add( number ) );
	}
	
	
	
	
	public static void main(String[] args){
	
	    RatNumber c = new RatNumber(3, 12); 
	
	    c.reduce();
	    c.ausgeben();
	
	    c.getStringRepresentation();
	    c.ausgeben3();
		
		RatNumber r = new RatNumber (5, 6);
		c.add(r);
		c.ausgeben2();
		
		
		
	}
}

Weil wenn ich das jetzt kompiliere wird mir der Fehler angezeigt:

C:\Users\C_I_N_J_A\Desktop\RatNumber.java:66: error: cannot find symbol
System.out.println( add( number ) );
^
symbol: variable number
location: class RatNumber
1 error
<<< Process finished. (Exit code 1)
 
Durch viel ausprobieren habe ich herausgefunden, wie ich mir das jetzt ausgeben lassen kann und jetzt sehe ich, dass ich c+c rechne und nicht wie gewollt c+r, aber warum?

So sieht das bei mir bisher aus:
Java:
class RatNumber{
     
	private int numerator;
	private int denominator;
	
	public RatNumber(int numerator, int denominator){
	     
		this.numerator = numerator;
		this.denominator = denominator;
		
		if (greatestCommonDivisor(numerator, denominator) != 1){
		     reduce();
		     }
	
	}
	
	public void ausgeben(){
	System.out.println( "Bruch: " + numerator + "/" + denominator);
	} 
	
	
	private static int greatestCommonDivisor(int a, int b) {
		while (a != b) {
			
			if (a > b)
				a = a - b;
			else
				b = b - a;
		}
		return a;
    }
	
	
	private void reduce(){
	
	    int a = greatestCommonDivisor(numerator, denominator);
	    numerator = numerator / a;
	    denominator = denominator / a;
	}
	
	
	public String getStringRepresentation(){
	
	    String string = ("" + numerator + "/" + denominator);
	    return string;
	}
	
	
	public void ausgeben3(){
	    System.out.println("Als Text: " + getStringRepresentation());
	}
	
	public RatNumber add(RatNumber number){
	
	    int nenner = this.denominator * denominator;
	    int thisZaehler = this.numerator * denominator;
	    int cZaehler = numerator * this.denominator;
	    int zaehlerSum = thisZaehler + cZaehler;
		
		RatNumber addZahl = new RatNumber ( zaehlerSum, nenner );
	
	return addZahl;
	}
	
	public void ausgebenAdd(){
	System.out.println( "Ergebnis Addition: " + numerator + "/" + denominator);
	} 
	
	
	
	
	public static void main(String[] args){
	
	    RatNumber c = new RatNumber(3, 12); 
	
	    c.reduce();
	    c.ausgeben();
	
	    c.getStringRepresentation();
	    c.ausgeben3();
		
		RatNumber r = new RatNumber (5, 6);
		RatNumber add = c.add(r);
		
		add.ausgebenAdd();
		
		
		
		
	}
}
 
Ich gebe dir jetzt mal einen Tipp, mit dem das eigentlich kein Problem sein dürfte:

Du baust deinen Bruch als separate Klasse auf und greifst aus einer anderen Klasse darauf zu. Das würde das Ganze ein bisschen leichter machen.
 
Ok, sollte nicht so schnell aufgeben...

Java:
public RatNumber add(RatNumber number){
	
	    int nenner = number.denominator * denominator;
	    int thisZaehler = number.numerator * denominator;
	    int cZaehler = numerator * number.denominator;
	    int zaehlerSum = thisZaehler + cZaehler;
		
		RatNumber addZahl = new RatNumber ( zaehlerSum, nenner );
	
	return addZahl;
	}

So müsste es doch jetzt stimmen oder? Also es gibt mir zumindest wie gewollt 13/12 aus.
 
Soweit ich das in meiner Aufgabenstellung verstehe, soll ich das aber alles nur in dieser einen Klasse machen...

Aber vielen Dank!
 
Hallo 🙂

Warum hast du denn so viele verschiende Methoden zur Ausgabe? Du hast ja die Methode getStringRepresentation - die Methoden ausgeben, ausgeben3 und ausgebenAdd sind daher unnötig, oder nicht? Du kannst ja in main einfach nach jeder Operation das machen:

Java:
RatNumber c = new RatNumber(3, 12);
// reduce solltest du in main gar nicht aufrufen, das ist eine interne Methode, die der Konstruktor ja eh aufruft
c.reduce();
System.out.println(c.getStringRepresentation());
          
RatNumber r = new RatNumber (5, 6);
RatNumber add = c.add(r);
System.out.println(add.getStringRepresentation());

Ich glaube, dass du an irgendeiner Stelle ein kleines Verständnisproblem bzgl. des Unterschieds zwischen Klasse und Objekt haben könntest. Kannst du mal beschreiben, warum du verschiedene Ausgabe-Methoden codiert hattest?
 
Bis grade eben hatte ich sogar noch mehr ausgabe-Methoden dadrin, auch dür sub, div, mult 😀
Ist das nicht so, dass die Klasse das ganze Ding ist, und die Objekte befinden sich in der Klasse?

Und so viele Ausgaben habe ich gemacht, weil ich dachte, dass man die braucht bei einer nicht static Methode, baer offenbar habe ich da falsch gedacht

Und so sieht's jetzt bei mir aus:
Java:
class RatNumber{
     
	private int numerator;
	private int denominator;
	
	public RatNumber(int numerator, int denominator){
	     
		this.numerator = numerator;
		this.denominator = denominator;
		
		if (greatestCommonDivisor(numerator, denominator) != 1){
		     reduce();
		     }
	
	}

	private static int greatestCommonDivisor(int a, int b) {
		while (a != b) {
			
			if (a > b)
				a = a - b;
			else
				b = b - a;
		}
		return a;
    }
	
	
	private void reduce(){
	
	    int a = greatestCommonDivisor(numerator, denominator);
	    numerator = numerator / a;
	    denominator = denominator / a;
	}
	
	
	public String getStringRepresentation(){
	
	    String string = ("" + numerator + "/" + denominator);
	    return string;
	}
	
	
	public RatNumber add(RatNumber number){
	
	    int nenner = number.denominator * denominator;
	    int thisZaehler = number.numerator * denominator;
	    int cZaehler = numerator * number.denominator;
	    int zaehlerSum = thisZaehler + cZaehler;
		
		RatNumber addZahl = new RatNumber ( zaehlerSum, nenner );
	
	return addZahl;
	}
	

    public RatNumber sub(RatNumber number){         
	
	    int nenner = number.denominator * denominator;
	    int thisZaehler = number.numerator * denominator;
	    int cZaehler = numerator * number.denominator;
	    int zaehlerSum = thisZaehler - cZaehler; 
        
        RatNumber subZahl = new RatNumber	( zaehlerSum, nenner );	
	
	return subZahl;
	}	
	
	
	public RatNumber mult(RatNumber number) {
	
	    int nenner = number.denominator * denominator;
	    int zaehler = number.numerator * numerator;
	    RatNumber multZahl = new RatNumber( zaehler, nenner);
	
	return multZahl;
	}

	
	public RatNumber div(RatNumber number) {
	
	    int nenner = number.numerator * denominator;
		int zaehler = number.denominator * numerator;
		RatNumber divZahl = new RatNumber( zaehler, nenner );
	
	return divZahl;
	}

	
	public static void main(String[] args){
	
	    RatNumber c = new RatNumber(3, 12); 
	
	    System.out.println("Bruch1: " + c.getStringRepresentation());
		
		RatNumber r = new RatNumber (5, 6);
		System.out.println("Bruch2: " + r.getStringRepresentation());
		
		RatNumber add = c.add(r);
		System.out.println("Ergebnis Addition: " + add.getStringRepresentation());
		
		RatNumber sub = c.sub(r);
		System.out.println("Ergebnis Subtraktion: " + sub.getStringRepresentation());
		
		RatNumber mult = c.mult(r);
		System.out.println("Ergebnis Multiplikation : " + mult.getStringRepresentation());
		
		RatNumber div = c.div(r);
		System.out.println("Ergebnis Division: " + div.getStringRepresentation());
		
		
	}
}

So wie ich das jetzt verstanden habe wird jetzt folgendes gemacht:

Also hier
Java:
RatNumber div = c.div(r);
		System.out.println("Ergebnis Division: " + div.getStringRepresentation());

wird das Ergebnis von c/r als div gespeichert, was ein Typ von RatNumber ist.
Dann wird mit div die Methode getStringRepresentation() aufgerufen, also wird div zu einem String umgewandelt und das wird dann ausgegeben?
Stimmt das so?
 
Oh und ich habe noch eine Frage, bin mir nicht ganz sicher ob ich das so richtig verstanden habe...

Das ist ja mein Konstruktor:

Java:
public RatNumber(int numerator, int denominator){
	     
		this.numerator = numerator;
		this.denominator = denominator;
		
		if (greatestCommonDivisor(numerator, denominator) != 1){
		     reduce();
		     }
	
	}

Und dadurch, dass da drinnen steht, dass der Bruch gekürzt werden soll, wird auch jeder neu erzeugte Bruch gekürzt, weil die ja immer über den Konstruktor erzeugt werden?
Stimmt das so?
 
Ah.. ihr habt bisher nur mit einer Klasse gearbeitet, oder? Deshalb wirkt es vielleicht sogar ein bisschen seltsam auf dich, dass man die Klasse überhaupt braucht - denn ihr benutzt sie ja einfach nur zum Sammeln von Methoden.

Eine Klasse kann man sich als Bauplan vorstellen - wenn du z.B. eine Klasse "Haus" hast, existiert dadurch noch kein Haus. Die ganzen Variablen wie "Anzahl der Zimmer" sind alle im Plan vorgesehen, aber noch existiert gar kein reales Haus. Durch "new" kann man in Java aus diesen Plänen konkrete Realisierungen der Pläne erstellen: Man bekommt dann ein Objekt der Klasse.

In deinem Programm existiert eine Klasse für Brüche, in main legst du durch "new" ein erstes Objekt "c" davon an. Dadurch wird Speicher für diesen Bruch reserviert und der Konstruktor speichert Zähler und Nenner dort. Weiter unten legst du mit "r" noch ein Objekt an - der Bruch "r" bekommt seinen ganz eigenen Speicherbereich und hat man mit "c" nichts zu tun, außer dass sie aus demselben Bauplan erzeugt wurden. Deshalb kann c auch nicht auf die Daten von r zugreifen und umgekehrt. Beide haben auch die gleichen Methoden, weil diese ja im Plan (der Klasse) vorhanden sind - aber wenn du c.getStringRepresentation aufrufst, passiert doch was anderes, als bei r.getStringRepresentation: Beide haben ihren eigenen Speicher, deshalb füllt die Methode den String auch mit anderen Daten.

Bei add passiert nun etwas Besonderes: Du übergibst das Objekt r an das Objekt c. Dadurch kann c plötzlich kurz (nur innerhalb der add-Methode) mit den Daten von r arbeiten. Du erzeugst damit ein neues Objekt und gibst es zurück - im Speicher existieren dann sogar 3 Objekte der Klasse. Aber da alle ihren eigenen Variablen haben, funktioniert getStringRepresentation trotzdem bei allen wie erwartet.

Ist es ein kleines bisschen klarer geworden? 🙂

Zu deinem zweiten Beitrag: Ja, das ist genau richtig - wenn es keinen anderen Konstrutkor gibt, muss jeder Bruch am Anfang seines Lebens diesen Code durchschreiten und wird dadurch direkt gekürzt. Also z.B. auch, wenn du in der add-Methode einen neuen Bruch erzeugst. Nur, wenn du manuell den Zähler und den Nenner änderst, müsstest du reduce direkt danach nochmal aufrufen, weil es dann ja nicht durch den Konstruktor ging.
 
Zuletzt bearbeitet:
Wow Danke, das ist wirklich sehr anschaulich erklärt und ich glaube ich habe das jetzt auch verstanden 🙂
Danke, danke, danke!!!!
 
Mhm ich habe das grade mal ausprobiert, mit 2 gleichen Brüchen (weil ich eine Methode schrieben soll, die ttrue zurückgibt falls die Brüche gleich sind) und jetzt kompiliert er das auch noch, gibt mir aber nur noch das Ergebnis bis zur Addition aus und ab der Subtraktion will er nichts mehr machen...
Woran liegt das?
 
Hm.. zeig mal den Code und erläutere, was "will er nichts mehr machen" heißt - gibt es eine Exception oder passiert einfach gar nichts mehr? Falls letzteres: beendet das Programm oder bleibt es stehen?
 
ich habs gefunden
lag an dem ggT

Java:
private static int greatestCommonDivisor(int a, int b) {
	
	    if (a== 0)
		    return 1;
		while (a != b) {
			
			if (a > b)
				a = a - b;
			else
				b = b - a;
		}
		return a;
    }

für den Fall a=0 gabs dann eine endlos Schleife...
 
Ja läuft, aber eine Division durch 0 fange ich nicht ab...
Das hab ich ganz vergessen, habs aber grade noch dazu gemacht 🙂
Dankeschön!
 
Zu deinem zweiten Beitrag: Ja, das ist genau richtig - wenn es keinen anderen Konstrutkor gibt, muss jeder Bruch am Anfang seines Lebens diesen Code durchschreiten und wird dadurch direkt gekürzt. Also z.B. auch, wenn du in der add-Methode einen neuen Bruch erzeugst. Nur, wenn du manuell den Zähler und den Nenner änderst, müsstest du reduce direkt danach nochmal aufrufen, weil es dann ja nicht durch den Konstruktor ging.

Gibt's nicht für jede Klasse den "leeren" Standardkonstruktor, also den ohne Parameter?
 

Neue Themen


Zurück
Oben