Hi,
ist es in Java wie in C++ (via Referenz) möglich in einer
Funktion/Methode Funktionsparameter "global" zu beeinflussen? Etwa in
diesem Sinne (so klappts allerdings nicht):
1
...
2
int a = 2;
3
foo(a);
4
System.out.print(a);
5
...
6
7
//foo()
8
public void foo(int a){
9
a=20;
10
}
11
...
Hier wird 2 ausgegeben. Ich suche nach einem Weg (ohne Arrays) den
Parameter über die Scopes der Methode zu beeinflussen. Entsprechend
sollte also 20 ausgegeben werden
Bin nicht der Experte, aber soweit ich weiß wird das sogar immer so
gemacht wie du es willst - bei Objekten. Bei primitiven Datentypen aber
nicht. Ersetzte einmal “int“ durch “Integer“ und versuch es nochmal.
Integer ist "imutable". Was einen natürlich nicht abhält einfach ein
"Austauschobjekt" zu nutzen (und sei es AtomicInteger), sinnvoller (und
universeller) wäre jedoch eine Rückgabe per return.
Läubi .. schrieb:> Integer ist "imutable".
Das macht doch nichts, oder? “a“ ist ja nur eine Referenz, ist doch egal
ob das Objekt dahinter geändert oder neu erzeugt wird.
>.Was einen natürlich nicht abhält einfach ein> "Austauschobjekt" zu nutzen (und sei es AtomicInteger),
Ist das wirklich notwendig?
> sinnvoller (und> universeller) wäre jedoch eine Rückgabe per return.
Gerade bei objektorientierter Programmierung ist das nicht immer
“besser“.
FBR schrieb:> Bin nicht der Experte, aber soweit ich weiß wird das sogar immer so> gemacht wie du es willst - bei Objekten. Bei primitiven Datentypen aber> nicht. Ersetzte einmal “int“ durch “Integer“ und versuch es nochmal.
Hätte ich auch erwartet mit meinen rudimentären Java-Kenntnissen, aber
iss nich!
Habe mal ein Mini-Javaprogramm gestrickt, das eine Zahl erstens als int
übergibt und ändern lässt, zweitens als Integer, und drittens als Obiekt
einer eigenen Klasse (MeinInteger).
Bei 1 & 2 wird der Wert beim Aufrufer nicht geändert, bei 3 dann doch:
MeinInteger.java:
1
publicclassMeinInteger
2
{
3
intget()
4
{
5
returnwert;
6
}
7
8
voidset(intneuerWert)
9
{
10
wert=neuerWert;
11
}
12
13
intwert;
14
};
t.java:
1
importjava.util.*;
2
3
publicclasst
4
{
5
staticvoidf_primitiv(inti)
6
{
7
i=42;
8
System.out.println("f_primitiv: i ist "+i);
9
}
10
11
staticvoidf_Integer(Integeri)
12
{
13
i=42;
14
System.out.println("f_Integer: i ist "+i);
15
}
16
17
staticvoidf_meinInteger(MeinIntegeri)
18
{
19
i.set(42);
20
System.out.println("f_meinInteger: i ist "+i.get());
21
}
22
23
publicstaticvoidmain(Stringargs[])
24
{
25
intint_zahl=1;
26
f_primitiv(int_zahl);
27
System.out.println("int_zahl ist "+int_zahl);
28
29
IntegerInteger_zahl=newInteger(1);
30
f_Integer(Integer_zahl);
31
System.out.println("Integer_zahl ist "+Integer_zahl);
32
33
MeinIntegermeinInteger=newMeinInteger();
34
meinInteger.set(1);
35
f_meinInteger(meinInteger);
36
System.out.println("meinInteger ist "+meinInteger.get());
37
}
38
39
}
Ausgabe:
1
klaus@vdr2:~ > javac t.java MeinInteger.java && java t
2
f_primitiv: i ist 42
3
int_zahl ist 1
4
f_Integer: i ist 42
5
Integer_zahl ist 1
6
f_meinInteger: i ist 42
7
meinInteger ist 42
Das ist doch krank.
Soll noch einer sagen, C++ wäre unlogisch.
Klaus Wachtler schrieb:> FBR schrieb:>> Bin nicht der Experte, aber soweit ich weiß wird das sogar immer so>> gemacht wie du es willst - bei Objekten. Bei primitiven Datentypen aber>> nicht. Ersetzte einmal “int“ durch “Integer“ und versuch es nochmal.>> Hätte ich auch erwartet mit meinen rudimentären Java-Kenntnissen, aber> iss nich!
Ich (der ebenfalls nicht viel Ahnung von Java hat ;-)) würde sagen, dass
in deinem Beispiel
1
staticvoidf_Integer(Integeri)
2
{
3
i=42;
4
System.out.println("f_Integer: i ist "+i);
5
}
mit i=42 ein neues Objekt angelegt wird, während das übegebene seinen
Wert behält. Da die Klasse Integer keine Set-Methode enthält, gibt es
keine Möglichkeit, ein Integer-Objekt zu modifizieren. Das ist wohl das,
was Läubi meinte mit
> Integer ist "imutable".
Klaus Wachtler schrieb:> Hätte ich auch erwartet mit meinen rudimentären Java-Kenntnissen,> aber iss nich!
Was hättest du in C++ den erwartet wenn du einer Referenzvariablen ein
neues Objekt zuweist?
Klaus Wachtler schrieb:> Das ist doch krank.
Nein ist es nicht. Du bekommst eine Kopie eines "Pointer" welcher auf
einen Integer zeigt und diesem weist du einen neuen Wert zu, wieso ist
deine Erwartung, dass sich der ursprünglich referenzierte Wert ändert.
> Soll noch einer sagen, C++ wäre unlogisch.
Das sit nicht unlogisch, sondern mit falschen Annahmen an ein nicht
existentes Problem herangegangen.
Bloss weil C/C++ es erlaubt bei (vermeintlichem) Bedarf beliebig im
Speicher herumzupfuschen muß das nicht für alle anderen Sprachen auch
gelten.
Klaus Wachtler schrieb:> und drittens als Obiekt> einer eigenen Klasse (MeinInteger)
Was die völlig korrekte Lösung ist (optimaler weise sollte deine Klasse
noch von Number ableiten ;-), vorausgesetzt der Anwendungsfall ist
genauso wie beschrieben und kein Fall von "premature optimization".
Nur der vollständigkeit halber, im zweiten Beispiel steht eigentlich
i = new Integer(42), was dir der Compiler netterweise abnimmt.
Yalu X. schrieb:> Ich würde sagen, dass> in deinem Beispiel> [...]
vollkommen korrekt.
FBR schrieb:> Gerade bei objektorientierter Programmierung ist das> nicht immer “besser“.
Erklärung? Klaus hat ja den "OO" Weg aufgezeigt das zu lösen, warum das
"direkte" manipulieren eines primitiven Datentyps (welcher in einer
perfekten OO-Welt nicht existieren dürfte) jetzt besser sein soll
erschließt sich mir nicht.
Yalu X. schrieb:> Da die Klasse Integer keine Set-Methode enthält, gibt es> keine Möglichkeit, ein Integer-Objekt zu modifizieren. Das ist wohl das,> was Läubi meinte mit>>> Integer ist "imutable".
Korrekt, außerdem ist Integer auch noch final, es kann also keine
"bösen" Ableitungen geben, welche dieses wieder umgehen, das ist aber
wirklich schon etwas weit gegriffen.
Läubi .. schrieb:> Was hättest du in C++ den erwartet wenn du einer Referenzvariablen ein> neues Objekt zuweist?
Meine Erwartung war, daß in der zweiten Funktion i eine Referenz ist auf
die Variable beim Aufrufer (soweit wohl auch noch richtig) und mit i=42
die Variable geändert wird (wohl falsch) - stattdessen wird das i der
Funktion von der Variablen beim Aufrufer entkoppelt und verweist auf ein
heimlich neu beschafftes Integer mit dem Wert 42, obwohl ich nirgends
ein new gemacht habe.
Dank meiner schweren C++-Kindheit ist das überraschend, weil eine
Referenz dort nie umgebogen wird.
Eine Zuweisung ändert dort einen Wert, nicht die Referenz (auch wenn
eine Initialisierung aussehen kann wie eine Zuweisung, das ist auch in
C++ unelegant).
Krank finde ich vor allem, daß primitive Datentypen generell anders
gehandhabt werden (call by value) als Objekte (call by reference).
Manchmal ist das eine sinnvoll, mal das andere, und bei primitiven eher
CBV. Aber deshalb komplett unterschiedliches Verhalten zu diktieren,
finde ich höchst unelegant seitens der Sprache.
Klaus Wachtler schrieb:> Aber deshalb komplett unterschiedliches Verhalten zu diktieren,> finde ich höchst unelegant seitens der Sprache
Das siehst du falsch! Das Verhalten ist in allen Fällen gleich, der
Unterschied ist der, dass alle primitiven (und alle Objektwrapper)
konstant sind und keine setter haben, in deinem Beispiel 3 wäre der
äquivalente Code:
1
staticvoidf_meinInteger(MeinIntegeri)
2
{
3
i=newMeinInteger()
4
i.set(42);
5
System.out.println("f_meinInteger: i ist "+i.get());
6
}
Und auch in diesem Falle würde die Funktion nicht das Funktionsargument
ändern (was sich auch nicht tut, sonder sie Ändert eben das Objekt auf
welches das Argument zeigt, wenn das Objekt setter hat. Wenn du diesem
(lokalem) "Zeiger" aber ein neues Objekt zuweist, sollte es nicht
verwunderlich sein, dass dies keine Auswirkung auf den Aufrufer hat.
Wenn man dieses verhindern möchte kann man den Parameter als final
deklarieren, was in deinem Beipiel 2 mit dem Integer zu einem
Compilefehler führt.
In C++ würde man wohl sagen, dass alle Referenzen auf primitive
Datentypen automatisch "const" sind.
Klaus Wachtler schrieb:> Dank meiner schweren C++-Kindheit ist das überraschend,> weil eine Referenz dort nie umgebogen wird.
... werden kann :-)
In Java ist das aber eher wie ein Pointer, welcher auf ein Objekt zeigt.
Klaus Wachtler schrieb:> obwohl ich nirgends> ein new gemacht habe.
Jeder der C++ macht sollte sich klar sein, das der Compiler halt ab und
an sachen "heimlich" macht und Objekte kopiert, oder es einem durch
typumwandlung "vereinfacht", und genau das ist hier der einzige Fall
in Java, nämlich das seit neuestem primitive Typen automatisch
"gewrappt" werden falls nötig.
http://docs.oracle.com/javase/1.5.0/docs/guide/language/autoboxing.html
Du kannst natürlich versuchen, mir das in schönen Worten schmackhaft zu
machen, aber:
Läubi .. schrieb:> In C++ würde man wohl sagen, dass alle Referenzen auf primitive> Datentypen automatisch "const" sind.
... überzeugt mich auch nicht vom kohärenten Verhalten zwischen
primitiven Daten und Objekten.
:-)
Klaus Wachtler schrieb:> ... überzeugt mich auch nicht vom kohärenten Verhalten zwischen> primitiven Daten und Objekten.
Das ist doch eigentlich nur die Besonderheit daß String (und auch
Integer wie ich gerade gelernt habe) immutable sind. Also nicht
veränderbar.
Das wurde (meines Erachtens) eingeführt, weil in Java eben alle Objekte
automatisch als Referenz übergeben werden und es aber zu ständigen
Fehlern kommt wenn diese Objekte verändert werden dürfen. Die
Alternative wäre, daß der Programmierer jeder Unterfunktion (von der er
nicht weiß was sie mit dem String macht eine Kopie übergeben müsste wenn
er keine Änderung wünscht.
Ja das ist eine Besonderheit und die muss man einfach lernen.
Sage mir nur C hätte keine Besonderheiten :-), ich sag nur daß z.B. ein
Struct quasi auch eine Objektdefinition ist nur mit Einschränkungen.
UR-Schmitt schrieb:> Das ist doch eigentlich nur die Besonderheit daß String (und auch> Integer wie ich gerade gelernt habe) immutable sind
Es gibt einfach nur keine Möglichkeit (wie in C/C++) eine
Speicherstelle zu modifizieren, also hat ein Aufrufer, welcher ein
Objekt/Datentyp/... erhält, welches keine Methoden oder public Member
hat um den inneren Zustand zu verändern nun mal keine Möglichkeit dies
zu tun.
Klaus Wachtler schrieb:> Du kannst natürlich versuchen, mir das in schönen Worten> schmackhaft zu machen
Ich wollte nur versuchen das Verhalten ein Stückweit zu erklären, um es
richtig zu Verstehen müsste man sich die VM Spezifikation mal zu Gemüt
führen (ist recht interessant).
UR-Schmitt schrieb:> Ja das ist eine Besonderheit und die muss man einfach lernen.
Wie gesagt, das ist nichts besonderes sondern ergibt sich rein aus den
"normalen" Umständen.
Wenn ich in C eine Funktion habe:
1
voidtuWas(inti){
2
i=7;
3
}
dann erwarte ich ja auch nicht, dass sich für den Aufrufer etwas
ändert. Das C halt (wie schon gesagt) noch zusätzlich die Möglichkeit
bietet direkt und ungeprüft in die Speicherzelle zu schreiben wo der
Aufrufer ggf. platz für einen int geschaffen hat ist etwas anderes...
Läubi .. schrieb:> In Java ist das aber eher wie ein Pointer, welcher auf ein Objekt zeigt.
Ja, ich glaube, es bringt viel für's Verständnis, wenn man die Java-
Referenzen mit den C++-Pointern gleichsetzt, denn die C++-Referenzen
sind wirklich etwas völlig anderes. Wenn man dann noch den '.'-Operator
in Java durch ein '->' in C++ ersetzt denkt, passt alles wieder perfekt:
- Dere Operator '=' kopiert keine Inhalte, sondern weist einer Variablen
einen neuen Objekt-Pointer zu. Genau wie in C++.
- Die Übergabe als Methodenargument erfolgt call-by-value. Genau wie in
C++.
- Der Operator '==' vergleicht den Speicherort und nicht den
Speicherinhalt. Genau wie in C++.
- ...
Der Hauptunterschied zwischen Java- und C++-Pointern besteht darin, dass
in Java sämtliche Operatoren zur Pointer-Arithmetik sowie zur Umwandlung
von Integers in Pointer und umgekehrt weggelassen wurden.
Läubi .. schrieb:> UR-Schmitt schrieb:>> Das ist doch eigentlich nur die Besonderheit daß String (und auch>> Integer wie ich gerade gelernt habe) immutable sind>> Es gibt einfach nur keine Möglichkeit (wie in C/C++) eine> Speicherstelle zu modifizieren, also hat ein Aufrufer, welcher ein> Objekt/Datentyp/... erhält, welches keine Methoden oder public Member> hat um den inneren Zustand zu verändern nun mal keine Möglichkeit dies> zu tun.
Stop, ich hatte mich jetzt nicht auf die automatische Kapselung von int
i durch ein Integer bezogen (Autoboxing), sondern darauf:
Klaus Wachtler schrieb:> Integer Integer_zahl = new Integer( 1 );> f_Integer( Integer_zahl );> System.out.println( "Integer_zahl ist " + Integer_zahl );
Es funktioniert ja auch nicht mit einem Integer Objekt. Der Grund ist
daß es immutable ist.
Wobei ich nicht weiß ob ich dieses Autoboxing als Vorteil sehen soll.
Yalu X. schrieb:> Ja, ich glaube, es bringt viel für's Verständnis, wenn man die Java-> Referenzen mit den C++-Pointern gleichsetzt, denn die C++-Referenzen> sind wirklich etwas völlig anderes.
Man sucht natürlich immer nach Analogien um sich das Lernen/Umsteigen
leichter zu machen.
Aber eigentlich wäre der saubere Weg möglichst gar nicht in Analogien zu
denken sondern die Sprache wie sie ist zu lernen.
Ja ich weiß, man macht das quasi automatisch :-)
> Nur der vollständigkeit halber, im zweiten Beispiel steht> eigentlich> i = new Integer(42)> was dir der Compiler netterweise abnimmt.
Eigentlich nicht.
Das steht eigentlich
i = Integer.ValueOf(42);
das Autoboxing.Und dieses Autoboxing bringt wieder seine eigenen
Probleme mit sich.
http://openbook.galileocomputing.de/javainsel9/javainsel_09_002.htm#mj9aaef31dce82a3daebaeb79ea69bfab2
(Scrollt auch noch ein wenig runter zum Abschnitt "Mehr Probleme als
Lösungen?".
Ohne jetzt Java-Bashing betreiben zu wollen, hab ich mir als C++
Programmierer dann doch verwundert die Augen gerieben, bei dem was ich
da gelesen habe. Wenn die Java Fraktion das nächste mal auf C++
herdrischt und alles als so kompliziert und unlogisch bezeichnet, reibe
ich denen das mal unter die Nase)
Was mir nicht klar ist:
Da treibt man Aufwand um die Basistypen in Klassen zu verpacken und dann
verhalten sich diese Klassen wieder ganz anders als die Klassen, die ich
selber schreibe. Wo ist da die Durchgängigkeit?
Karl Heinz Buchegger schrieb:> Eigentlich nicht.> Das steht eigentlich
Was nur eine Optimierung für kleine Zahlen ist.
Karl Heinz Buchegger schrieb:> hab ich mir als C++> Programmierer dann doch verwundert die Augen gerieben
Wieso? Es folgt alles den ganz normalen Java Regeln. Und ich kann mich
noch an ein C++ Beispiel vor kurzem hier erinnern wo sich die C++
eingeweihten selbst verwundert die Augen gerieben haben, es gibt wohl in
jeder Sprache Eigenheiten die einen als Aussenstehender verwundern.
Karl Heinz Buchegger schrieb:> Da treibt man Aufwand um die Basistypen in Klassen zu verpacken und dann> verhalten sich diese Klassen wieder ganz anders als die Klassen, die ich> selber schreibe. Wo ist da die Durchgängigkeit?
Sie verhalten sich nicht anders sondern haben einfach nur die
Eigenschaft, dass bei Operationen diese automatisch in den Primitiven
typ gewandelt werden oder zurück. Wer das nicht mag, darf in der IDE
oder mit externen Tools gerne Sicherstellen dass sowas in "seinem" Code
nicht benutzt wird.
Läubi .. schrieb:> Karl Heinz Buchegger schrieb:>> Eigentlich nicht.>> Das steht eigentlich>> Was nur eine Optimierung für kleine Zahlen ist.
Hab ich zuerst auch gedacht. Dem ist aber nicht so.
>> Programmierer dann doch verwundert die Augen gerieben>> Wieso? Es folgt alles den ganz normalen Java Regeln.
Schon richtig. So sind die Regeln. Aber die muss man im Eifer des
Gefechts beachten. Und wo da die Regellogik bei
1
Integer n1 = new Integer( 10 );
2
Integer n2 = Integer.valueOf( 10 );
3
Integer n3 = 10;
4
Integer n4 = 10;
5
System.out.println( n1 == n2 ); // false
6
System.out.println( n2 == n3 ); // true
7
System.out.println( n1 == n3 ); // false
8
System.out.println( n3 == n4 ); // true
ist, sehe ich ehrlich gesagt nicht wirklich.
So was ist ausser fehleranfällig doch nur noch fehleranfällig.
Oder das hier
1
Integer k1 = 127;
2
Integer k2 = 127;
3
System.out.println( k1 == k2 ); // true
4
Integer l1 = 128;
5
Integer l2 = 128;
6
System.out.println( l1 == l2 ); // false
(für alle Nicht-Link Leser. Es hängt vom tatsächlichen Wert ab, warum
das einemal true rauskommt und das andere mal false. Das verhält sich
für kleine Zahlen anders als für große Zahlen)
> Und ich kann mich> noch an ein C++ Beispiel vor kurzem hier erinnern wo sich die C++> eingeweihten selbst verwundert die Augen gerieben haben, es gibt wohl in> jeder Sprache Eigenheiten die einen als Aussenstehender verwundern.
Zweifellos.
Vielleicht bewerte ich das auch über und in der Praxis ist das kein
großes Problem (programmiere nicht in Java). Trotzdem kann ich mich
damit ehrlich gesagt nicht anfreunden.
Karl Heinz Buchegger schrieb:> Hab ich zuerst auch gedacht. Dem ist aber nicht so.
Doch ist es, siehe API Dokumentation.
Karl Heinz Buchegger schrieb:> So was ist ausser fehleranfällig doch nur noch fehleranfällig
Nein Objekte werden nicht per == verglichen sonst gibt es murks (s.o.)!
Karl Heinz Buchegger schrieb:> Und wo da die Regellogik bei ist> Integer n1 = new Integer( 10 );
Es wird ein neues Objekt angelegt welches (angenommen) an Speicherstelle
1 liegt.
> Integer n2 = Integer.valueOf( 10 );
Es wird ein (von der valueOf Funktion gechacheds) Objekt zurückgegeben
welches (angenommen) an der Speicherstelle 2 liegt.
> Integer n3 = 10;> Integer n4 = 10;
Equivalent zu n2 = Integer.valueOf( 10 ), allso zeigen n3 und n4 auf
Speicherstelle 2
Und wenn man das jetzt ersetzt steht im Folgenden:
Karl Heinz Buchegger schrieb:> System.out.println( 1 == 2 ); // false> System.out.println( 2 == 2 ); // true> System.out.println( 1 == 2 ); // false> System.out.println( 2 == 2 ); // true
Was ist daran unlogisch? "Unlogisch" ist nur, das bei solchen Beispielen
immer implizit die implementierung der Sun JRE angenommen wird, welche
nämlich wirklich die 256 ersten Zahlen cached, das wird von der API
aber überhauptnicht gefordert.
>> as this method is likely to yield significantly>> better space and time performance by caching frequently>> requested valueshttp://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Integer.html#valueOf%28int%29
Es wäre also durchaus auch legitim, das die Methode ALLE jemals
angeforderten Integer instanzen cached und diese dort zurückgibt, in
diesem Fall wäre das Ergebnis im Beispiel immer true!
Karl Heinz Buchegger schrieb:> Oder das hier
Siehe oben, hier wird auf ein Spezifikum der JRE Implementierung von Sun
vertraut.
Läubi .. schrieb:>> Was ist daran unlogisch?
Offenbar haben wir verschiedene Vorstellungen davon, was man alles im
Hinterkopf behalten muss, wenn man Programmcode liest. :-)
> "Unlogisch" ist nur, das bei solchen Beispielen> immer implizit die implementierung der Sun JRE angenommen wird, welche> nämlich wirklich die 256 ersten Zahlen cached, das wird von der *API*> aber überhauptnicht gefordert.
?
Das macht es ja dann noch schlimmer, wenn dieses Problem auftreten kann
aber nicht muss.
Ok, ich hör auf. Ich versteh von Java zu wenig und wollte nur meine
Verwunderung darüber Ausdruck verleihen.
Gibt es eigentlich einen Grund, warum Integer immutable ist?
Karl Heinz Buchegger schrieb:> Ohne jetzt Java-Bashing betreiben zu wollen, hab ich mir als C++> Programmierer dann doch verwundert die Augen gerieben,
Ich auch wie oben schon gesagt. Als ich das gesehen habe war mein erster
Gedanke: "Was haben die geraucht".
Aber das habe ich auch gedacht, als ich das erste mal den
Referenzoperator in C++ gesehen habe, oder daß ein struct jetzt eine
Lightwight Klasse sein soll...
Ich habe Autoboxing nie vermisst und werde es wenn irgendmöglich
weiterhin vermeiden.
Karl Heinz Buchegger schrieb:> Vielleicht bewerte ich das auch über und in der Praxis ist das kein> großes Problem
Kann zu fies zu findenen Fehlern führen und bringt wie gesagt für mich
eigentlich keine Vorteile. (Programmiere fast ausschließlich in Java)
Karl Heinz Buchegger schrieb:> was man alles im> Hinterkopf behalten muss
Man sollte wissen, dass man keine Pointer/Referenzen mit == vergleicht,
in C geht das auch schief wenn ich zwei Pointer vergleiche die zwar
beide auf einen Int mit dem Wert 5 zeigen aber an unterschiedlicher
Adresse...
Karl Heinz Buchegger schrieb:> Das macht es ja dann noch schlimmer, wenn dieses Problem> auftreten kann aber nicht muss.
Nein, das ist einfach ein Programmierfehler, s. o. es ist nicht Aufgabe
der Sprache jede Dummheit vom Programmierer fernzuhalten.
Karl Heinz Buchegger schrieb:> Gibt es eigentlich einen Grund, warum Integer immutable ist?
Es gibt Fälle wo man ein Objekt Anstelle eines primitiven typen
benötigt, dafür sollen die Wrappetypes dienen, und da eine 7 eben eine 7
ist und nicht aufeinmal eine 99 hat man sich dafür entschieden, ist in
der Praxis aber auch nahezu nie ein Problem, es steht jedem frei sich
"seinen" MutableInteger zu definieren, und diesen von Number abzuleiten.
Läubi .. schrieb:> Man sollte wissen, dass man keine Pointer/Referenzen mit == vergleicht
In einer Sprache wie Java, die explizit versucht, Implementierungs-
aspekte vom Programmierer fernzuhalten, sollte es eigentlich den
'=='-Operator für Referenzen (bzw. Pointer) gar nicht geben. Er stellt
insofern einen Grenzfall zur (in Java nicht vorgesehenen) Pointer-
Arithmetik dar, dass er zwar keinen Pointer als Ergebnis liefert, das
Ergebnis jedoch von der Art und Weise, wie Objekte im Speicher abgelegt
werden, abhängt.
Vermutlich lautet die Regel so:
- Wenn zwei Objekte unterschiedlichen Inhalt haben, liefert '==' immer
false.
- Wenn das eine Objekt durch direkte oder indirekte Zuweisung mit '='
oder die Übergabe als Methodenargument aus dem anderen Objekt
entsteht, liefert '==' immer true.
- In allen anderen Fällen ist ist das Ergebnis von '==' implementations-
abhängig.
Aber genau dieses "implementationsabhängig" hat in Java eigentlich
nichts zu suchen, zumal der Compiler diesen dritten Fall nicht ohne
weiteres erkennen und deswegen nicht einmal eine entsprechende Warnung
ausgeben kann.
UR-Schmitt schrieb:> Aber eigentlich wäre der saubere Weg möglichst gar nicht in Analogien zu> denken sondern die Sprache wie sie ist zu lernen.
Ich finde das Denken in Analogien schon sinnvoll (nicht nur bei der
Programmierung), da es eine Art Wiederverwendung von Wissen darstellt.
> Ja ich weiß, man macht das quasi automatisch :-)
Ich mache das sogar bewusst ;-)
Gerade wenn ein C++-Programmierer versucht, sich in Java einzuarbeiten,
ist es meiner Meinung nach unklug, bei null anzufangen. Viel besser ist
es, sich erst einmal darüber zu informieren, was in Java gleich wie in
C++ ist, und das ist sehr viel.
Natürlich gibt es nicht zu jedem Sprachelement in Java ein Analogon in
C++, sonst gäbe es keinen Grund, mit Java anzufangen. Deswegen sollte
man nicht tagelang nach Analogien suchen, wo möglicherweise gar keine
sind. Sonst blockiert man sich nur unnötig.
Nach dem man sich einen Überblick über die Gemeinsamkeiten der beiden
Sprachen verschafft hat, schaut man sich anschließend den Rest, also die
Unterschiede und Erweiterungen gegenüber C++ an. Dabei überlegt man sich
(oder liest es nach), was die Entwickler von Java wohl dazu bewegt hat,
diese Deltas einzuführen. Wenn man diese Gründe verstanden hat, hat man
auch Java als Ganzes verstanden.
Gerade durch die Konzentration auf die Unterschiede wird man ein echter
Java-Programmierer und nicht einer, der lediglich C++-Denkweisen in Java
umsetzt, frei nach dem Motto "You can write Fortran in any language".
Zudem kommt man mit dieser Vorgehensweise schneller zum Ziel, als wenn
man erst einmal alles, was man über Programmierung weiß, beiseite legt
und wie ein völliges Greenhorn an die Sache heran geht.
Läubi .. schrieb:> Karl Heinz Buchegger schrieb:>> was man alles im>> Hinterkopf behalten muss> Man sollte wissen, dass man keine Pointer/Referenzen mit == vergleicht,> in C geht das auch schief wenn ich zwei Pointer vergleiche die zwar> beide auf einen Int mit dem Wert 5 zeigen aber an unterschiedlicher> Adresse...
Naja, bei C++ gibt es eben eine genaue Trennung, welche Operationen sich
auf den Zeiger und welche auf das Objekt auswirkt. Bei Java wirken sich
dann die Operationen eigentlich implizit auf das Objekt aus, außer halt
bei ==, da ist es dann stattdessen doch wieder der Zeiger.
Wie ist das eigentlich bei den primitiven Typen? Werden bei dem
folgenden auch Adressen vergleichen, oder ist es da dann wieder anders?
1
inti=2;
2
intj=2;
3
System.out.println(i==j);
> Karl Heinz Buchegger schrieb:>> Das macht es ja dann noch schlimmer, wenn dieses Problem>> auftreten kann aber nicht muss.> Nein, das ist einfach ein Programmierfehler, s. o. es ist nicht Aufgabe> der Sprache jede Dummheit vom Programmierer fernzuhalten.
Ich dachte immer, daß sich Java genau das als Aufgabe gesetzt hat.
> Karl Heinz Buchegger schrieb:>> Gibt es eigentlich einen Grund, warum Integer immutable ist?> Es gibt Fälle wo man ein Objekt Anstelle eines primitiven typen> benötigt,
Schon hier kommt bei mir die Frage auf, warum ich überhaupt eine Klasse
statt eines int brauchen könnte, um einen int-Wert zu speichern, also
warum die primitiven Typen irgendwie anders sind.
> dafür sollen die Wrappetypes dienen, und da eine 7 eben eine 7> ist und nicht aufeinmal eine 99 hat man sich dafür entschieden,
Das Argument leuchtet mir nicht ein. Das macht man bei den primitiven
Typen ja auch nicht so - wäre ja auch unsinnig (int i = 7, und dann muß
i für immer und ewig 7 bleiben, "weil eine 7 eben eine 7 ist"...)
> ist in der Praxis aber auch nahezu nie ein Problem, es steht jedem frei> sich "seinen" MutableInteger zu definieren, und diesen von Number> abzuleiten.
Dafür einen extra Typ definieren zu müssen finde ich aber doch etwas
umständlich für so was simples.
Rolf Magnus schrieb:>>> Gibt es eigentlich einen Grund, warum Integer immutable ist?>> Es gibt Fälle wo man ein Objekt Anstelle eines primitiven typen>> benötigt,>> Schon hier kommt bei mir die Frage auf, warum ich überhaupt eine Klasse> statt eines int brauchen könnte, um einen int-Wert zu speichern, also> warum die primitiven Typen irgendwie anders sind.
Da hatte man wohl (und ich denke nicht so ganz zu unrecht) Angst vor
Performance-Verlusten.
>> dafür sollen die Wrappetypes dienen, und da eine 7 eben eine 7>> ist und nicht aufeinmal eine 99 hat man sich dafür entschieden,>> Das Argument leuchtet mir nicht ein.
Mir auch nicht.
Aber ok, Java ist nicht meine Sprache und wird es auch nie werden.
(Und wenn ich eine Konklusio aus diesem Tread mitnehme, dann die, dass
MS bei C# wohl einiges 'richtiger' gemacht hat. Nicht das ich jetzt zu
einem C# Fan mutiert wäre, so ist das dann auch wieder nicht. Aber ein
Blick über den Zaun hat noch niemandem geschadet. Und schliesslich gibt
es ja auch in C++ jede Menge Kritikpunkte)
Yalu X. schrieb:> In einer Sprache wie Java, die explizit versucht, Implementierungs-> aspekte vom Programmierer fernzuhalten, sollte es eigentlich den> '=='-Operator für Referenzen (bzw. Pointer) gar nicht geben
Manchmal benötigt man das, und es ist durchaus legitim, z.B. wenn du nun
wissen möchtest ob es sich und das selbe und nicht nur um das
gleiche Objekt handelt.
Yalu X. schrieb:> Vermutlich lautet die Regel so
Nein, es gibt nur eine definierte Menge von Operatoren auf Datentypen,
und für Object ist es halt so definiert, das die Objektidentität
verglichen wird.
Yalu X. schrieb:> Aber genau dieses "implementationsabhängig"
Es ist nicht implementationsabhängig, das ganze ist ein konstruiertes
Beispiel wo etwas anderes herauskommt bei einer Operation als man im
ersten Augenblick bei nicht genauem hinschauen erwarten würde.
Rolf Magnus schrieb:> Naja, bei C++ gibt es eben eine genaue Trennung,> welche Operationen sich auf den Zeiger und welche> auf das Objekt auswirkt
Gibt es bei Java auch, nur weil man sie nicht kennt, oder sie zufällig
gleich der ist welche man von andere Seite her kennt heist nicht das es
unlogisch ist.
> Bei Java wirken sich dann die Operationen> eigentlich implizit auf das Objekt aus
Nein! Es gibt (s.o.) eine definierte Liste von definierten Operatoren.
Rolf Magnus schrieb:> Wie ist das eigentlich bei den primitiven Typen? Werden bei dem> folgenden auch Adressen vergleichen, oder ist es da dann wieder anders?
Es ist nicht "wieder" anders, sondern es wird die auf int definierte
Operation == durchgeführt. Diese ist aber nicht die gleiche wie für
Object und auch nicht die gleiche wie z.B. für boolean.
Rolf Magnus schrieb:> Schon hier kommt bei mir die Frage auf, warum ich überhaupt eine Klasse> statt eines int brauchen könnte,
Z.B. analog wie in C++, weil eine int variable nicht 'null' sein kann
oder eine Rückgabe einer Funktion die int zurückgibt nicht 'null' sein
kann.
Rolf Magnus schrieb:> Das Argument leuchtet mir nicht ein
Variable != Objekt/Wert. Schreib doch mal in C rein 7 = 5, da wird sich
der Compiler auch beschweren, da man dem "Wert" 7 keinen neue Bedeutung
zuweisen kann, das hat man halt versucht nachzubauen, insbesondere da es
in Java kein 'const' gibt.
Rolf Magnus schrieb:> Dafür einen extra Typ definieren zu müssen finde ich aber> doch etwas umständlich
Was ist den "Dafür", der TE schreibt ja nicht mal wozu er das haben
will, diese Operation "Speicherzelle eines Int" an eine Funktion zu
übergeben gibt es schlich nicht, und man benötigt sie auch in der Regel
nicht. Und für die 0,001% der Fälle wo es dann doch unumgänglich wäre
kann man sich dann halt behelfen, wie gesagt es gibt schon diese Typen
siehe
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/atomic/AtomicInteger.html
auch wenn dieser noch mehr bietet und nicht automatischem (un)boxing
unterliegt.
Karl Heinz Buchegger schrieb:> Und wenn ich eine Konklusio aus diesem Tread mitnehme, dann die, dass> MS bei C# wohl einiges 'richtiger' gemacht hat.
Wenn man das daran festmacht, dass man einem Int per modifizierbarem
pointer übergeben kann mag das wohl stimmen, in "normalen" Java
Anwendungen arbeitet man aber mit Objekten und nicht mit einzelnene
int's die nicht per returnvalue übergeben werden können...
Läubi .. schrieb:> Yalu X. schrieb:>> Vermutlich lautet die Regel so> Nein, es gibt nur eine definierte Menge von Operatoren auf Datentypen,> und für Object ist es halt so definiert, das die Objektidentität> verglichen wird.
Ja schon. Aber wann sind zwei Objekte a und b identisch? Klar, wenn sie
an der gleichen Stelle im Speicher liegen.
Aber wann ist dies der Fall? Das wollte ich mit den aufgestellten Regeln
beschreiben. Ob das tatsächlich so gemacht wird, dazu müsste man sich
die Sprachspezifiktion von Java genauer ansehen.
> Yalu X. schrieb:>> Aber genau dieses "implementationsabhängig"> Es ist nicht implementationsabhängig, das ganze ist ein konstruiertes> Beispiel wo etwas anderes herauskommt bei einer Operation als man im> ersten Augenblick bei nicht genauem hinschauen erwarten würde.
Ist denn garantiert, dass der Methodenaufruf Integer.valueOf(n) für
gleiche n immer die gleiche Objektreferenz zurückgibt?
Zumindest die API-Dokumentation macht hierzu keine definitive Aussage:
"Returns a Integer instance representing the specified int value. If a
new Integer instance is not required, this method should generally be
used in preference to the constructor Integer(int), as this method is
likely to yield significantly better space and time performance by
caching frequently requested values."
Das hört sich für mich so an, dass die Gleichheit der zurückgegebenen
Referenzen u.a. davon abhängt, ob noch genügend Platz im Cache ist.
Somit ist doch die '=='-Operation für Referenzen implementationsabhän-
gig, wenn nicht sogar undefiniert?
Yalu X. schrieb:> Somit ist doch die '=='-Operation für Referenzen implementationsabhän-> gig, wenn nicht sogar undefiniert?
Diese Schlussfolgerung verstehe ich nicht :-)
Die API sagt nur, dass entweder eine gechachte Instanz zurückgegeben
wird oder eine neue erzeugt wird. Die '=='-Operation wird dadurch
nicht beeinflusst.
Wenn ich zwei Objekte habe (hier sind es zufällig Integer, welche
auch als int repräsentiert werden könnten) vergleichen möchte mache
ich das eben über die equals Methode, dann kommt hier auch genau das
erwartete Verhalten raus.
Der Fehler ist einfach, das hier Objektreferenzen verglichen werden
obwohl Objektwerte verglichen werden sollten (oder dem Leser suggeriert
wird=.
Yalu X. schrieb:> Aber wann sind zwei Objekte a und b identisch? Klar, wenn sie> an der gleichen Stelle im Speicher liegen
Da es ja scheinbar doch jetzt genau interessiert:
>> 15.21.3. Reference Equality Operators == and !=>> At run-time, the result of == is true if the operand>> values are both null or both refer to the same object or>> array; otherwise, the result is false.http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html
Wie festgestellt wird ob es sich um das selbe Objekt handelt bleibt
der VM überlassen (das könnte z.B. eine ID sein welche intern gehalten
wird), ich meine gelesen zu haben das in der OracleVM halt eine Art
SpeicherstellenID genutzt wird.
Läubi .. schrieb:> Da es ja scheinbar doch jetzt genau interessiert:
Ok, vermutlich reden wir deswegen aneinander vorbei, weil ich noch nicht
ganz verstanden habe, wozu der '=='-Operator in der Praxis genutzt wird.
Ich habe gedacht, dass er primär ein effizienter Ersatz für equals ist,
wobei aber für die Anwendbarkeit gewisse Voraussetzungen erfüllt sein
müssen, da sonst u.U. inhaltlich gleiche Objekte als verschieden angese-
hen werden. Wenn das aber tatsächlich die Intention '==' wäre, müssten
diese Voraussetzungen ja irgendwo niedergeschrieben sein.
Aber wahrscheinlich wird der Operator tatsächlich nur so verwendet, wie
du es zitiert hast, nämlich um festzustellen, dass zwei Objekte "the
same" sind. Nur, wozu braucht man so etwas? Hast du vielleicht ein
typisches Beispiel parat?
Eines, das mir auf die Schnelle einfällt, wäre folgendes:
1
if(a==b||a.equals(b))
2
// a und b sind inhaltlich gleich
Das Ganze wäre ein zu equals äquivalenter, aber evtl. effizienterer
Vergleich. Wobei eine gute Implementation von equals den '=='-Vergleich
aber ja schon beinhalten könnte.
Yalu X. schrieb:> Ich habe gedacht, dass er primär ein effizienter Ersatz für equals ist
Wie du schon geschrieben hast ist er das eben nicht, das sind zwei
paar Schuhe.
Yalu X. schrieb:> Wobei eine gute Implementation von equals den '=='-Vergleich> aber ja schon beinhalten könnte.
Die default implementierung für equals sieht so aus:
1
returnthis==otherObject;
Yalu X. schrieb:> Das Ganze wäre ein zu equals äquivalenter, aber evtl.> effizienterer Vergleich
Genau so ist es. Man benötigt sowas hauptsächlich in Bibliotheken,
brauchen tut man das auch, wenn Objekte zwar "gleich" sind, du aber den
Index eines bestimmten Objektes ermitteln willst, im OSGi Framework wird
es benötigt für set/unset Methoden welche eventuell ein replacement
Objekt bereitstellen, sicher nichts was man ständig braucht, aber es
gibt durchaus legitime Anwendungsfälle.
Yalu X. schrieb:> Ok, vermutlich reden wir deswegen aneinander vorbei, weil ich noch nicht> ganz verstanden habe, wozu der '=='-Operator in der Praxis genutzt wird.
na ja er wird halt häufig bei primitiven Typen wie int, long genutzt.
Insofern ist ein == bei Integer Objekten durchaus ein Fehler der einem
passieren kann.
Das ist halt der etwas hybride Ansatz von Java mit den primitiven Typen
und den Objekten. Smalltalk hat das meines WIssens nicht, dafür ist
smalltalk dann auch deutlich langsamer, whärend man in Java richtig
schnelle Routinen programmieren kann. (ja ich weiß C (++) ist schneller,
aber ich hatte Beispiele wo quasi identischer C code ohne Optimierung
auf Windows langsamer war).
Yalu X. schrieb:> Eines, das mir auf die Schnelle einfällt, wäre folgendes:> if(a == b || a.equals(b))> // a und b sind inhaltlich gleich>> Das Ganze wäre ein zu equals äquivalenter, aber evtl. effizienterer> Vergleich. Wobei eine gute Implementation von equals den '=='-Vergleich> aber ja schon beinhalten könnte.
Wobei die direkte Lösung dann den Vorteil hätte daß man sich den
Methodenaufruf spart, das ganze bei einer zeitkritischen Routine
effizienter wäre.
Läubi .. schrieb:> Also ist C genauso unlogisch wie Java ;-)
Wieso das?
Du kannst in C/C++ Zeiger vergleichen, oder Werte.
Natürlich können zwei Zeiger unterschiedlich sein, aber trotzdem auf
Elemente mit gleichem Wert verweisen. Das ist aber nicht überraschend
m.E..
Es erscheint nur unlogisch, weil du etwas dreist schreibst:
1
printf("n1 = %i\n",*n1);
2
...
Also den Text n1 nimmst, aber den Wert *n1 ausgibst.
Mit den drei Ausgaben von n1, n2, n3 wird eine Gleichheit suggeriert,
die es nicht gibt, nur um dann hinterher stolz festzustellen, daß n1==n2
nicht gilt.
Das Unlogische an Java ist, daß man mit demselben == mal den Wert
vergleicht (bei primitiven Datentypen) und mal die Identität feststellt,
nicht die Gleichheit des Werts (bei Objekten), und mal ist es
Glückssache, ob durch Optimierung zwei Dinge identisch sind oder nicht
(jaja, ist nicht unlogisch, sondern wohldefiniert...)
Ebenso wie mal CBV und mal CBR bei Parameterübergabe, je nach Typ.
Wenn man dem Programmierer schon nicht die Wahl zwischen CBV und CBR
lässt, weil der durchschnittliche Coder es eh nicht rafft, dann hätte
man ihm solche Feinheiten auch nicht zumuten sollen.
Abgesehen davon: Der Vergleich mit C und C++ ist insofern unfair, als C
aus der Steinzeit kommt und deshalb bis zu C++ noch viele Altlasten
mitschleppt.
Dagegen ist Java wesentlich neuer und hatte die Chance, alles besser zu
machen; zumal es auch noch wesentlich HW-abstrahierter ist.
Dann hätte man solche Ecken viellcith nicht unbedingt einbauen müssen.
Ich habe auch nie behauptet, daß C und C++ besonders saubere Sprachen
wären - nur, daß Java auch seine Leichen im Keller hat.
Läubi .. schrieb:> Also ist C genauso unlogisch wie Java ;-)
Darauf kann man sich einigen - sie nehmen sich nix :-)
Aber wie gesagt, habe ich ja eh keine Ahnung :-)
Klaus Wachtler schrieb:> Dagegen ist Java wesentlich neuer und hatte die Chance, alles besser zu> machen; zumal es auch noch wesentlich HW-abstrahierter ist.> Dann hätte man solche Ecken viellcith nicht unbedingt einbauen müssen.
Wie gesagt das wurde der Performance geschuldet.
Wie ist das eigentlich in c#? Ich habe da bis jetzt nur etwas gelesen
und gespielt?
Klaus Wachtler schrieb:> Wieso das?> Du kannst in C/C++ Zeiger vergleichen, oder Werte.> Natürlich können zwei Zeiger unterschiedlich sein, aber trotzdem auf> Elemente mit gleichem Wert verweisen.
Und genau das ist hier auch das (vermeintliche) Problem bei den
merkwürdig Anmutenden Beispielen, ich wollte nur Zeigen, dass man in C
genauso "unlogische" Probleme konstruieren kann.
Läubi .. schrieb:> So ich habe mal ein Beispiel in C zusammengehackt, welches das gleiche> "Problem" hat:[c]#include <stdio.h>> Also ist C genauso unlogisch wie Java ;-)
Nö.
Denn dieses Beispiel hast du extra so zurecht gemacht. Das ist nichts
was dir vom System so vorgesetzt wird, ob du willst oder nicht.
Was natürlich nicht heißt, dass C keine Ecken und Kanten und
Merkwürdigkeiten hat.
Klaus Wachtler schrieb:> Das Unlogische an Java ist, daß man mit demselben == mal den Wert> vergleicht (bei primitiven Datentypen) und mal die Identität feststellt,> Ebenso wie mal CBV und mal CBR bei Parameterübergabe, je nach Typ.
Wie ich weiter oben schrieb: Betrachte die Java-Objektvariablen einfach
als Pointer (implementierungstechnisch sind sie das ja auch), dann lösen
sich diese Widersprüche in Wohlgefallen auf.
Karl Heinz Buchegger schrieb:> Denn dieses Beispiel hast du extra so zurecht gemacht. Das ist nichts> was dir vom System so vorgesetzt wird, ob du willst oder nicht.
In Java setzt dir das System auch nichts vor. Es liegt an dir == für
primitive Typen und die euqals bzw. compare Methode für Objekte zu
benutzen. Java ist hier eher konsequenter als C(++) das dir alle
möglichen Verwirrungen incl operator overloading zulässt.
Einige Ausnahme: Autoboxing.
Yalu X. schrieb:> Klaus Wachtler schrieb:>> Das Unlogische an Java ist, daß man mit demselben == mal den Wert>> vergleicht (bei primitiven Datentypen) und mal die Identität feststellt,>>> Ebenso wie mal CBV und mal CBR bei Parameterübergabe, je nach Typ.>> Wie ich weiter oben schrieb: Betrachte die Java-Objektvariablen einfach> als Pointer (implementierungstechnisch sind sie das ja auch), dann lösen> sich diese Widersprüche in Wohlgefallen auf.
Yalu, das ist (denke ich) den meisten schon klar. Und zumindest mir
gehts da gar nicht mehr drum.
Was mir missfällt ist folgendes.
Angenommen ich habe eine Funktion (Java Syntax Fehler jetzt bitte zu
entschuldigen)
void foo( int i, int j )
{
if( i == j )
mach was
else
mach was anderes
}
dann hat die eine klare Semantik. Ändere ich jetzt das int um auf
Integer
void foo( Integer i, Integer j )
{
if( i == j )
mach was
else
mach was anderes
}
dann ändert sich die Semantik (== macht bei Integer was anderes als bei
int). Und wenn ich das nicht bedenke oder sehe, dann hab ich einen
schwerwiegenden Fehler eingebaut durch eine eigentlich auf den ersten
Blick harmlose Modifikation. (Integer sei jetzt Synonym für andere
Klassentypen).
In C++ ist das nicht so. == ist immer ein Wertevergleich. Und wenn meine
Variablen Pointer sind, dann vergleiche ich immer noch Werte, nämlich
die in den Pointer Variablen gespeicherten Adressen (sind ja auch 'nur'
Werte). C++ macht mir aus
i == j
kein
&i == &j
abhängig davon, ob i,j primitive Datentypen sind oder ausgewachsene
Klassen. Und scheinbar sehe nicht nur ich das als unlogisch an.
Ich hätte es logischer empfunden, wenn == immer einen Wertevergleich
macht und es im Falle von Klassen dann eben eine spezielle Funktion
gibt, die aufgerufen wird und den Wertevergleich macht. Dann hätte man
auch gleich die Problematik "Was macht man mit < <= >= >" auch gleich
unter Dach und Fach - oder gibts da bei Klassen was? Konnte auf die
Schnelle nichts finden.) Und um festzustellen, ob 2 Objekte identisch
sind, gibt es dann eben eine isIdent() Member Funktion, die das zu
bewerkstelligen hat.
Aber egal. Die Dinge sind, wie sie sind.
Karl Heinz Buchegger schrieb:> In C++ ist das nicht so. == ist immer ein Wertevergleich. Und wenn meine> Variablen Pointer sind, dann vergleiche ich immer noch Werte, nämlich> die in den Pointer Variablen gespeicherten Adressen (sind ja auch 'nur'> Werte). C++ macht mir aus>> i == j>> kein> &i == &j
Macht dir Java auch nicht.
Was dich stört ist daß bei Objekten eben implizit eine Adresse auf ein
Objekt übergeben wird, man muss primitive Typen und Objekte eben
wirklich konsequent unterscheiden.
Wenn ich nur daran denke was ich früher an wildem Unsinn mit Operator
Overloading in C++ gesehen habe, dann ist das in Java aber nur ein
klitzkleines Problem.
Überall, wo ein Array-Name als R-Value (und nicht als Operand von
sizeof) auftaucht, wird er wie ein Pointer auf das erste Array-Element
behandelt. Java geht einen Schritt weiter und tut das, was C[++] mit
Arrays macht, mit allen zusammengesetzten Datentypen (wobei in Java
auch Arrays Objekte sind, so dass sich die Unterscheidung zwischen Array
und Objekt ohnehin erübrigt). Das ist in meinen Augen konsistenter.
> Und wenn meine Variablen Pointer sind, dann vergleiche ich immer noch> Werte, nämlich die in den Pointer Variablen gespeicherten Adressen> (sind ja auch 'nur' Werte).
Das macht doch Java ebenso (vorausgesetzt man betrachtet Objektvariablen
als Pointer).
UR-Schmitt schrieb:> Karl Heinz Buchegger schrieb:>> In C++ ist das nicht so. == ist immer ein Wertevergleich. Und wenn meine>> Variablen Pointer sind, dann vergleiche ich immer noch Werte, nämlich>> die in den Pointer Variablen gespeicherten Adressen (sind ja auch 'nur'>> Werte). C++ macht mir aus>>>> i == j>>>> kein>> &i == &j>> Macht dir Java auch nicht.
Schon klar. Weil es die & Operation dort nicht gibt. Aber genau darauf
läuft es hinaus. Da Wertevergleich - dort Adressenvergleich.
> Wenn ich nur daran denke was ich früher an wildem Unsinn mit Operator> Overloading in C++ gesehen habe,
Ooooohhhh, jaaaaaa
Da kann man sich ganz schnell in die Probleme rein-katapultieren :-)
> dann ist das in Java aber nur ein> klitzkleines Problem.
Muss ich dir glauben und glaub ich dir auch.
Karl Heinz Buchegger schrieb:> "Was macht man mit < <= >= >"
Dafür gibt es das Interface Comparable welches, oh Wunder :-) von
Integer implementiert wird.
Karl Heinz Buchegger schrieb:> dann ändert sich die Semantik
Es ändert sich insbesondere die Semantik von der ganzen Funktion, was
machst du wenn jetzt eines der Argumente null ist? Ich kann mir nicht
vorstellen, dass in C++ das nicht auch auf einen NullPointerAccess
hinauslaufen würde, die Änderung ist also in jedem Falle nicht ohne
Anpassen/Nachdenken "einfach so" anpassbar.
Wenn man dann seine IDE richtig einstellt wird man auch eine passende
Warnung erhalten.
Tuschee.
An Arrays hab ich jetzt nicht gedacht. Punkt für dich. Arrays sind
sowohl in C als auch in C++ Bastarde.
Aber spätestens bei
std::vector<int> a;
std::vectot<int> b;
if( a == b )
sind wir wieder auf Wertevergleichen. (Und ja: IN C++ gibts auch ein
std::array, welches sich 'korrekt' verhält)
> Es ändert sich insbesondere die Semantik von der ganzen Funktion,> was machst du wenn jetzt eines der Argumente null ist? Ich kann mir> nicht vorstellen, dass in C++ das nicht auch auf einen> NullPointerAccess hinauslaufen würde
Ändere ich
void foo( int i, int j )
{
if( i == j )
mach was
else
mach was anderes
}
auf
void foo( std::string i, std::string j )
{
if( i == j )
mach was
else
mach was anderes
}
ändert sich an der Semantik gar nichts. Noch nicht mal ein
void foo( std::string& i, std::string& j )
{
if( i == j )
mach was
else
mach was anderes
}
muss mir (an dieser Stelle) Kopfzerbrechen machen, weil eine Referenz
immer auf ein existierendes Objekt zeigen muss. Wenn hier also ein null
Pointer im Spiel ist, dann hat der Aufrufer einen Fehler gemacht. (Was
nicht heißt das eine entsprechende Überprüfung nicht sinnvoll wäre. Aber
in C+ ist es auf legalem Weg nicht möglich eine Referenz auf ein
NULL-Objekt zu erzeugen)
Erst wenn ich auf
void foo( std::string * i, std::string * j )
{
if( *i == *j )
mach was
else
mach was anderes
}
gehe, muss ich mir in der Funktion Gedanken über mögliche null Pointer
machen. Da hab ich aber nicht einfach nur einen primtiven Typ gegen
einen Klassentyp ausgetauscht, sondern durch den Pointer der ganzen
Funktion eine ganz andere Wendung gegeben.
(Ja ich weiß schon. du wirst jetzt sagen, dass das ja genau das ist was
in Java 'under the hood' gemacht wird. Und ja, ich geb dir ja auch
recht. Nur muss ich in C++ da schon ein wenig mehr machen als nur 1 Wort
gegen ein anderes austauschen. C++ macht mir da etwas mehr bewusst, dass
hier etwas komplett anderes abgeht)
Wichtig ist sowas zb bei Templates, bei denen es fatal wäre, wenn ein ==
in einem Template je nachdem ob man das für einen primtiven Typ oder
einen Klassentyp instaziiert sich unterschiedlich verhält.
Karl Heinz Buchegger schrieb:> muss mir (an dieser Stelle) Kopfzerbrechen machen, weil eine Referenz> immer auf ein existierendes Objekt zeigen muss
Wie gesagt, in Java gilt dies nicht! Wenn dann müßtest du dein Beispiel
von int, int auf int*, int* ändern und dann hättest du das Problem.
Läubi .. schrieb:> Karl Heinz Buchegger schrieb:>> muss mir (an dieser Stelle) Kopfzerbrechen machen, weil eine Referenz>> immer auf ein existierendes Objekt zeigen muss>> Wie gesagt, in Java gilt dies nicht! Wenn dann müßtest du dein Beispiel> von int, int auf int*, int* ändern und dann hättest du das Problem.
Yep.
Und da ist dann eben der Unterschied der, dass mir C bzw. C++ hier etwas
mehr bewusst macht, dass da plötzlich etwas anderes abgeht. Mit dem
Austausch eines Datentyps durch einen Pointer-Typ ist es nicht getan. Da
fallen Änderungen an, auf die mich der COmpiler bei Nichtbeachtung
aufmerksam macht (zb. das sich beim Aufrufer etwas ändern muss)
Aber ich klink mich jetzt aus.
Ich denke die Standpunkte sind klar. Da jetzt großartig diskutieren
bringt auch nichts, denn wir bestimmen nicht die Sprachrichtung.
Danke für ein paar Einsichten in Java.
Karl Heinz Buchegger schrieb:> Mit dem Austausch eines Datentyps durch einen Pointer-Typ> ist es nicht getan
Das ist doch bei Java ebenso...
Karl Heinz Buchegger schrieb:> auf die mich der COmpiler bei Nichtbeachtung> aufmerksam macht (zb. das sich beim Aufrufer etwas ändern muss)
Hier dito, es gibt wie gesagt den einen Spezialfall, dass dann der
Aufrufer nichts ändern braucht, da autoboxing geschieht, aber ob der
Compiler das tut oder nicht, entbindet dich in keinster Weise davon zu
prüfen ob deine Änderung der Funktionsparameter nicht auch eine Änderung
der Funktion zur Folge hat.
Wenn das Wirklich eine Funktion ist, welche nicht von den Argumenten
abhängt sondern nur davon ob diese gleichwertig sind, könnte man doch
gleich auf equals oder auf Compareable gehen, und könnte dann sogar
beliebige Eingaben erlauben. Die ganze "Problematik" ist überhaupt gar
keine, außer das sich Leute berufen fühlen daraus imaginäre Probleme zu
konstruieren.
Läubi .. schrieb:> Rolf Magnus schrieb:>> Naja, bei C++ gibt es eben eine genaue Trennung,>> welche Operationen sich auf den Zeiger und welche>> auf das Objekt auswirkt> Gibt es bei Java auch,
Allerdings reicht es da nicht, nur die Operation zu betrachten, sondern
es hängt auch noch vom Typ ab. Das war es, was ich damit meinte.
> nur weil man sie nicht kennt, oder sie zufällig gleich der ist welche man> von andere Seite her kennt heist nicht das es unlogisch ist.
Und nur weil sie logisch ist, muß sie nicht gut sein.
> Rolf Magnus schrieb:>> Wie ist das eigentlich bei den primitiven Typen? Werden bei dem>> folgenden auch Adressen vergleichen, oder ist es da dann wieder anders?> Es ist nicht "wieder" anders, sondern es wird die auf int definierte> Operation == durchgeführt. Diese ist aber nicht die gleiche wie für> Object und auch nicht die gleiche wie z.B. für boolean.
Also ist sie doch eine andere als für Objekte, und genau das halte ich
für keine gute Idee. Natürlich ist es nicht unlogisch, aber unintuitiv.
Rein interessehalber: Was macht diese Operation denn für boolean, wenn
nicht die Werte vergleichen?
> Rolf Magnus schrieb:>> Schon hier kommt bei mir die Frage auf, warum ich überhaupt eine Klasse>> statt eines int brauchen könnte,> Z.B. analog wie in C++, weil eine int variable nicht 'null' sein kann> oder eine Rückgabe einer Funktion die int zurückgibt nicht 'null' sein> kann.
Ok. Ich dachte, die Gründe wären etwas weitreichender.
> Rolf Magnus schrieb:>> Das Argument leuchtet mir nicht ein> Variable != Objekt/Wert. Schreib doch mal in C rein 7 = 5, da wird sich> der Compiler auch beschweren, da man dem "Wert" 7 keinen neue Bedeutung> zuweisen kann, das hat man halt versucht nachzubauen, insbesondere da es> in Java kein 'const' gibt.
Du willst damit doch nicht etwa sagen, daß es ein wesentlicher
Anwendungsfall von Integer ist, int-Konstanten zu definieren?
> Rolf Magnus schrieb:>> Dafür einen extra Typ definieren zu müssen finde ich aber>> doch etwas umständlich> Was ist den "Dafür", der TE schreibt ja nicht mal wozu er das haben> will, diese Operation "Speicherzelle eines Int" an eine Funktion zu> übergeben gibt es schlich nicht, und man benötigt sie auch in der Regel> nicht.
Wo steht denn was von Speicherzellen? Er will einfach nur einen int
modifizieren, den er per Referenz in die Funktion reinbekommt, so wie
man das mit Objekten auch ständig macht. Das klingt mir nicht so weit
hergeholt. In C++ übergebe ich halt eine Referenz auf den int, und bei
einem Objekt übergebe ich eine Referenz auf das Objekt, und fertig.
Rolf Magnus schrieb:>> Rolf Magnus schrieb:>>> Schon hier kommt bei mir die Frage auf, warum ich überhaupt eine Klasse>>> statt eines int brauchen könnte,>> Z.B. analog wie in C++, weil eine int variable nicht 'null' sein kann>> oder eine Rückgabe einer Funktion die int zurückgibt nicht 'null' sein>> kann.>> Ok. Ich dachte, die Gründe wären etwas weitreichender.
Ich glaube, der Hauptgrund für die Existenz der Integer-Klasse ist die
Vervollkommnung des Prinzips "everything is an object". Das hat nicht
nur ästhetische, sondern vor allem auch praktische Vorteile:
In Java sind alle Klassen direkt oder indirekt von der Klasse "Object"
abgeleitet. Man kann sehr allgemeingehaltene Klassen und Methoden
schreiben, wenn man die verwendeten Membervariablen bzw. Methodenargu-
mente als Object deklariert. M.W. waren in früheren Java-Versionen die
Container-Datentypen (Listen, Bäume usw.) genau so realisiert. Da die
Werte primitiver Datentypen aber keine Objekte sind, benötigt man
Wrapper-Klassen, um auch sie in gleicher Weise verwenden zu können.
Mit der Einführung von Generics¹ dürfte sich der Bedarf nach Objekten
der Klasse Object etwas gelegt haben. Trotzdem kann es manchmal sinnvoll
sein, Container anzulegen, bei denen die einzelnen Elemente unterschied-
lichen Typs sein können. Das geht aber nur, wenn die einzelnen Typen
eine gemeinsame Basisklasse haben, und die ist eben in vielen Fällen
Object. Sollen nur numerische Werte gespeichert werden, wäre die gemein-
same Basisklasse stattdessen Number, von denen bspw. Integer, Double,
und BigInteger abgeleitet sind.
Ich verstehe allerdings nicht ganz, warum man überhaupt die primitiven
Datentypen von C++ übernommen hat. Der Effizienzgewinn sollte eigentlich
kein Grund gewesen sein, denn es wäre in einer stark typisierten Sprache
wie Java sicher möglich gewesen, die gleiche Effizienz durch eine Son-
derbehandlung der Klassen Integer, Double usw. vonseiten des Compilers
zu erreichen. Aber so tief stecke ich in dem Java-Gedöns nicht drin, um
das sicher beurteilen zu können :)
————————————
¹) Diese entsprechen in etwa den Templates in C++.
Yalu X. schrieb:> Ich glaube, der Hauptgrund für die Existenz der Integer-Klasse ist die> Vervollkommnung des Prinzips "everything is an object".
Der Grund ist doch einfach, daß man ints und andere primitive Typen so
nicht in den Klassen wie dem Collection Framework benutzen könnte.
Yalu X. schrieb:> Ich verstehe allerdings nicht ganz, warum man überhaupt die primitiven> Datentypen von C++ übernommen hat. Der Effizienzgewinn sollte eigentlich> kein Grund gewesen sein,
Na ja wenn du sehr rechenintensive Anwendungen mit vielen char oder int
Vergleichen / sortierungen etc hast dann dürfte sich das massiv
bemerkbar machen. Man sollte nicht vergessen wie alt Java inzwischen
schon ist und wie langsam damals die Rechner noch waren.
Performance war der große Kritikpunkt aller objektorientierten Sprachen
und einer der Gründe daß C++ als sehr performante Hybridsprache so viel
Zuspruch erfahren hat.
Yalu X. schrieb:> die gleiche Effizienz durch eine Son-> derbehandlung der Klassen Integer, Double usw. vonseiten des Compilers> zu erreichen.
Hmm, keine Ahnung ich habe von Compilerbau keine Ahnung, aber trivial
ist das bestimmt nicht.
Egal, für uns ist Java ein riesengroßer Segen, alle Kritiker sollen mir
mal das C++ Programm zeigen, das relativ performant in mehreren
unterschiedlichen Application Server lauffähig ist und mit EINER
Auslieferung für alle Unix Derivate (incl AIX und Solaris), für Windows
und (mit Tricks) sogar auf dem Host läuft, und eine leistungsfähige
Datenbankanbindung an Oracle, MSSQLServer oder DB/2 hat. Wenn nötig
sogar an unterschiedliche DBMS gleichzeitig!
Zusätzlich war es relativ leicht eine MQ Anbindung zu bekommen (auch
unabhängig vom Betriebssystem).
In unseren C Programmen heisst das etwa 20 unterschiedliche Builds
durchzuführen, jede Version/Patch in den 20 Auslieferungen vorzuhalten,
die Builds zu testen usw. usw. Und dabei sind dann noch nicht mal alle
Kombinationen vorrätig und kein MQ.
Schönen Abend :-)
Rolf Magnus schrieb:
> Ok. Ich dachte, die Gründe wären etwas weitreichender.
Yalu X. schrieb:
> Ich glaube, der Hauptgrund für die Existenz der Integer-Klasse ist die> Vervollkommnung des Prinzips "everything is an object".
Udo Schmitt schrieb:
> Der Grund ist doch einfach, daß man ints und andere primitive Typen so> nicht in den Klassen wie dem Collection Framework benutzen könnte.
Es gibt natürlich noch sehr viel mehr Gründe, ich denke eine
vollständige Liste wird man nicht aufstellen könne, z.B. kann man sehr
schön Algorithmen auf der abstrakten Klasse Number aufbauen, der kann
man dann shorts/ints/floats/double/BigInt/BigDecimal/... übergeben.
Yalu X. schrieb:> Trotzdem kann es manchmal sinnvoll> sein, Container anzulegen, bei denen die einzelnen Elemente unterschied-> lichen Typs sein können.
Ja z.B. eine Liste von Numbers ;-) aber gerade für Generics benötigt
man halt diese "Objektprimitiven"...
Yalu X. schrieb:> Ich verstehe allerdings nicht ganz, warum man überhaupt> die primitiven Datentypen von C++ übernommen hat
Ob man die von C++ übernommen hat sei mal dahingestellt, intern ist es
halt so, dass die VM Operationen auf int/long und float/double
definiert. Ich kann mir vorstellen das es einmal "Gewöhnung" war, jeder
"kennt" halt so Basistypen, andererseits hat man halt so ein bischen ein
Henne/Ei Problem:
Woraus soll das "Urobjekt" für Zahlen bestehen wenn es nichts gibt außer
Object? Klar könnte man es wie bei String machen, aber selbst String
baut schon auf den "primitiven" byte und char auf...
Yalu X. schrieb:> denn es wäre in einer stark typisierten Sprache> wie Java sicher möglich gewesen, die gleiche Effizienz> durch eine Sonderbehandlung der Klassen Integer, Double> usw. vonseiten des Compilers zu erreichen
Jein, sicher wäre das "irgendwie" möglich gewesen, aber ein Objekt ist
immer mit etwas mehr Aufwand verbunden, besonders die Stackoperationen
würden dann auf der Abstrakten(!) VM sehr viel komplexer zu beschreiben
sein, da man sehr viele Sonderfälle hätte.
Wer sich das genauer ansehen will kann sich entweder obiges Dokument zu
Gemüt führen, oder mal in die doc32049.pdf von ATMEL reinschnuppern, da
wird das JVM Modul des AVR32 AP7000 beschrieben, ein teil der JVM
instruktionen wird direkt von der CPU ausgeführt, ein teil durch
Traps/Eventhandler... und das sind z.B. jene welche sich um die
Erzeugung von Objekten drehen.
Eine (vereinfachte) JVM in Hardware kann man z.B. bei Tanenbaum
bewundern: http://en.wikipedia.org/wiki/IJVM die hab ich mal in VHDL
nachgebaut, da weiß man dann zu schätzen was der arme PC tagtäglich
leisten muss ;-)