Dlaczego == porównanie wartości ciągu operatora nie dotarło do Javy?

50

Każdy kompetentny programista Java wie, że musisz użyć String.equals (), aby porównać ciąg, zamiast ==, ponieważ == sprawdza równość referencji.

Kiedy mam do czynienia z łańcuchami, przez większość czasu sprawdzam równość wartości zamiast równości odniesienia. Wydaje mi się, że byłoby bardziej intuicyjne, gdyby język pozwalał na porównywanie wartości ciągów za pomocą ==.

Dla porównania operator C # == sprawdza równość wartości dla ciągu s. A jeśli naprawdę potrzebujesz sprawdzić równość referencji, możesz użyć String.ReferenceEquals.

Inną ważną kwestią jest to, że ciągi są niezmienne, więc zezwolenie na tę funkcję nie jest szkodliwe.

Czy jest jakiś szczególny powód, dla którego nie jest to zaimplementowane w Javie?

L46kok
źródło
12
Możesz przyjrzeć się Scali, gdzie ==jest równość obiektu i eqrówność odniesienia ( ofps.oreilly.com/titles/9780596155957/… ).
Giorgio
Dla przypomnienia, to może ci nie pomóc, ale o ile pamiętam, możesz porównać literały łańcuchowe z „==”
Kgrover
10
@Kgrover: możesz, ale to tylko wygodny produkt uboczny równości referencji i tego, jak Java agresywnie optymalizuje ciąg literałów dopasowujących ciąg do referencji do tego samego obiektu. Innymi słowy, działa, ale z niewłaściwych powodów.
tdammers
1
@aviv ==operator mapuje tylko, Equalsjeśli ==operator został zaimplementowany w ten sposób. Domyślne zachowanie dla ==jest takie samo jak ReferenceEquals(w rzeczywistości ReferenceEqualsjest zdefiniowane jako wersja obiektowa ==)
Patrick Huizinga
3
Jest to konsekwencja decyzji projektowych, które mają wiele sensu w wielu innych scenariuszach. Ale ponieważ wydaje się, że o tym wiesz i pytasz o to, czuję się zmuszony odpowiedzieć na twoje pytanie: Dlaczego musi istnieć przypadek użycia dla porównania referencji String?
Jacob Raihle,

Odpowiedzi:

90

Myślę, że to tylko konsekwencja, czyli „zasada najmniejszego zdziwienia”. Łańcuch jest przedmiotem, więc byłoby zaskakujące, gdyby traktowano go inaczej niż inne obiekty.

W momencie, gdy pojawiła się Java (~ 1995), posiadanie czegoś takiego Stringbyło całkowitym luksusem dla większości programistów, którzy byli przyzwyczajeni do przedstawiania łańcuchów jako tablic zakończonych zerem. Stringzachowanie było teraz tym, czym było wtedy i to dobrze; subtelna zmiana zachowania później może mieć zaskakujące, niepożądane efekty w działających programach.

Na marginesie można użyć String.intern()kanonicznej (internowanej) reprezentacji łańcucha, po czym można dokonać porównań ==. Internowanie zajmuje trochę czasu, ale potem porównania będą naprawdę szybkie.

Dodanie: w przeciwieństwie do niektórych odpowiedzi, nie chodzi o wspieranie przeciążania operatora . +Operator (konkatenacji) działa na Strings chociaż Java nie obsługuje operatora przeciążenia; jest to po prostu traktowane jako specjalny przypadek w kompilatorze, rozwiązując StringBuilder.append(). Podobnie ==można było potraktować jako specjalny przypadek.

Dlaczego więc zadziwić specjalnym przypadkiem, +ale nie ==? Ponieważ +po prostu nie kompiluje się po zastosowaniu do Stringobiektów niebędących obiektami, więc szybko to widać. Odmienne zachowanie od ==byłyby znacznie mniej widoczne, a tym samym znacznie bardziej zdumiewające, gdy uderza.

Joonas Pulakka
źródło
8
Przypadki szczególne dodają zdziwienia.
Blrfl
17
Struny były luksusem w 1995 roku? Naprawdę?? Spójrz na historię języków komputerowych. Liczba języków, które miały w tym czasie jakiś ciąg znaków, znacznie przewyższałaby liczbę tych, które nie miały. Ile języków oprócz C i jego potomków używało tablic zakończonych zerami?
WarrenT 03.04.13
14
@WarrenT: Pewnie, niektóre (jeśli nie większość) języków miało jakiś ciąg znaków, ale myślę, że w 1995 roku ciągi znaków zbierane przez śmieci, obsługujące Unicode, były nowością. Na przykład, Python wprowadził ciągi Unicode w wersji 2.0, rok 2000. Wybór niezmienności był wówczas również kontrowersyjnym wyborem.
Joonas Pulakka
3
@JoonasPulakka Więc może powinieneś edytować swoją odpowiedź, aby to powiedzieć. Ponieważ na obecnym etapie odpowiedź „całkowity luksus” jest dość błędna.
svick
1
Interning ma swój koszt: dostajesz ciąg, który nigdy nie zostanie zwolniony. (Cóż, chyba że użyjesz własnego silnika internowania, który możesz wyrzucić.)
Donal Fellows
32

James Gosling , twórca Javy, wyjaśnił to w lipcu 2000 roku :

Pominąłem przeciążanie operatora jako dość osobisty wybór, ponieważ widziałem, jak zbyt wiele osób nadużywało go w C ++. Spędziłem dużo czasu w ciągu ostatnich pięciu do sześciu lat badając ludzi na temat przeciążenia operatora i to jest naprawdę fascynujące, ponieważ dzielisz społeczność na trzy części: Prawdopodobnie około 20 do 30 procent populacji myśli o przeciążeniu operatora jako spawn diabła; ktoś zrobił coś z przeciążeniem operatora, co tak naprawdę je odznaczało, ponieważ użyli + do wstawiania listy i to sprawia, że ​​życie jest naprawdę, bardzo mylące. Wiele z tego problemu wynika z faktu, że istnieje tylko około pół tuzina operatorów, których można rozsądnie przeciążyć, a jednak istnieją tysiące lub miliony operatorów, które ludzie chcieliby zdefiniować - więc musisz wybrać,

Ross Patterson
źródło
50
Ach, tak, stare „pozwala” stępić spiczaste narzędzie, aby gafy się nie skrzywdziły ”wymówka.
Blrfl
22
@Blrfl: Jeśli narzędzie stwarza więcej problemów niż rozwiązuje, nie jest dobrym narzędziem. Oczywiście decyzja, czy tak jest w przypadku przeciążenia operatora, może przerodzić się w bardzo długą dyskusję.
Giorgio
15
-1. To wcale nie odpowiada na pytanie. Java nie mają przeciążanie operatorów. ==Operator jest przeciążony obiektów i pierwotnych. +Operator jest przeciążony na byte, short, int, long, float, double, Stringi prawdopodobnie kilka innych zapomniałem. Byłoby idealnie można przeciążyć ==dla Stringtak dobrze.
Jörg W Mittag
10
@Jorg - nie, nie robi. Przeciążenie operatora jest niemożliwe do zdefiniowania na poziomie użytkownika. Rzeczywiście w kompilatorze są pewne szczególne przypadki, ale to prawie się nie kwalifikuje
AZ01
9
@Blrfl: Nie przeszkadza mi, że sami sobie krzywdzą. Zdenerwowałem się, gdy przypadkowo wysunęli mi oko.
Jonas
9

Spójność w języku. Posiadanie operatora, który działa inaczej, może zaskoczyć programistę. Java nie pozwala użytkownikom przeciążać operatorów - dlatego równość odwołań jest jedynym rozsądnym znaczeniem ==między obiektami.

W Javie:

  • Między typami ==numerycznymi porównuje równość liczbową
  • Między typami ==boolowskimi porównuje równość boolowską
  • Między obiektami ==porównuje tożsamość odniesienia
    • Służy .equals(Object o)do porównywania wartości

Otóż ​​to. Prosta reguła i prosta identyfikacja tego, czego chcesz. Wszystko to opisano w sekcji 15.21 JLS . Zawiera trzy podrozdziały, które są łatwe do zrozumienia, wdrożenia i uzasadnienia.

Gdy pozwolisz na przeładowanie== , dokładne zachowanie nie jest czymś, co możesz spojrzeć na JLS i położyć palec na konkretnym elemencie i powiedzieć „tak to działa”, kod może być trudny do uzasadnienia. Dokładne zachowanie ==może być zaskakujące dla użytkownika. Za każdym razem, gdy go zobaczysz, musisz wrócić i sprawdzić, co to właściwie oznacza.

Ponieważ Java nie pozwala na przeciążanie operatorów, potrzebny jest test równości wartości, który można zastąpić podstawową definicją. Tak więc było to wymagane przez te wybory projektowe. ==w testach Java numeryczne dla typów liczbowych, równość boolowska dla typów boolowskich i równość referencyjna dla wszystkich innych elementów (które mogą zastąpić .equals(Object o)robienie, co chcą, dla równości wartości).

Nie jest to kwestia „czy istnieje przypadek użycia konkretnej konsekwencji tej decyzji projektowej”, ale raczej „jest to decyzja projektowa mająca na celu ułatwienie tych innych rzeczy, jest to jej konsekwencja”.

Internowanie ciągów , jest jednym z takich przykładów. Zgodnie z JLS 3.10.5 wszystkie literały łańcuchowe są internowane. Inne łańcuchy są internalizowane, jeśli ktoś .intern()je wywołuje . To "foo" == "foo"prawda, jest konsekwencją decyzji projektowych podejmowanych w celu zminimalizowania zajmowanego miejsca w pamięci przez literały String. Poza tym internowanie ciągów jest czymś na poziomie JVM, który ma trochę ekspozycji na użytkownika, ale w przeważającej większości przypadków nie powinno być czymś, co dotyczy programisty (a przypadki użycia dla programistów nie były coś, co było wysoko na liście projektantów przy rozważaniu tej funkcji).

Ludzie to zauważą +i +=są przeciążeni ciągiem. Tego jednak nie ma ani tu, ani tam. Pozostaje przypadek, że jeśli ==ma wartość równości wartości dla String (i tylko String), potrzebna byłaby inna metoda (która istnieje tylko w String) dla równości odniesienia. Co więcej, niepotrzebnie skomplikowałoby to metody, które pobierają Object i oczekują, że będą ==się zachowywać w jeden sposób, a .equals()inne będą wymagały od użytkowników specjalnego przypadku wszystkich tych metod dla String.

Konsekwentna umowa ==dotycząca Obiektów polega na tym, że jest to tylko równość odniesienia i .equals(Object o)istnieje dla wszystkich obiektów, które powinny testować równość wartości. Skomplikowanie tego komplikuje zdecydowanie zbyt wiele rzeczy.


źródło
dzięki za poświęcenie czasu na odpowiedź. To byłaby świetna odpowiedź na inne pytania, z którymi się połączyłem. Niestety nie jest to odpowiednie dla tego pytania. Zaktualizuję OP o wyjaśnienia na podstawie komentarzy. Szukam więcej przypadków użycia, w których użytkownik języka chciałby mieć fałszywe negatywy podczas porównywania ciągów. Język zapewnia tę funkcję jako spójność, teraz chciałbym, abyśmy poszli o krok dalej. Być może jest to potrzebne od projektanta w nowym języku? (niestety, brak lang-design.SE)
Anonsage
3
@ Anonsage nie jest fałszywie ujemny. Nie są tym samym przedmiotem. To wszystko, co mówi. Muszę również zaznaczyć, że w Javie 8 new String("foo") == new String("foo")może być prawdziwa (patrz Deduplikacja ciągów ).
1
Jeśli chodzi o projektowanie języka, CS.SE reklamuje, że może tam być na temat.
och, dziękuję! Zamieszczę tam moje przyszłe pytania dotyczące projektowania języka. :) I, tak, niestety „fałszywie negatywny” nie jest najdokładniejszym sposobem opisania mojego pytania i tego, czego szukam. Muszę napisać więcej słów, aby ludzie nie musieli zgadywać próbuję powiedzieć.
Anonsage,
2
„Spójność w języku” pomaga również w przypadku generyków
Brendan,
2

Java nie obsługuje przeciążania operatora, co oznacza, ==że dotyczy tylko typów pierwotnych lub referencji. Wszystko inne wymaga wywołania metody. Dlaczego projektanci to zrobili, to pytanie, na które tylko oni mogą odpowiedzieć. Gdybym musiał zgadywać, to prawdopodobnie dlatego, że przeciążenie operatora powoduje złożoność, której nie byli zainteresowani dodaniem.

Nie jestem ekspertem w języku C #, ale wydaje się, że projektanci tego języka skonfigurowali go tak, aby każdy element pierwotny był structi każdy structbył przedmiotem. Ponieważ C # pozwala na przeciążanie operatora, takie ustawienie sprawia, że ​​każda klasa, nie tylko String, może pracować w „oczekiwany” sposób z dowolnym operatorem. C ++ pozwala na to samo.

Blrfl
źródło
1
„Java nie obsługuje przeciążania operatora, co oznacza, że ​​== dotyczy tylko pierwotnych typów lub referencji. Cokolwiek innego wymaga wywołania metody.”: Można dodać, że jeśli ==oznaczałoby to równość łańcuchów, potrzebowalibyśmy innej notacji dla równości referencji.
Giorgio
@Giorgio: Dokładnie. Zobacz mój komentarz do odpowiedzi Gilada Naamana.
Blrfl
Chociaż można to rozwiązać za pomocą metody statycznej, która porównuje referencje dwóch obiektów (lub operatora). Na przykład jak w C #.
Gilad Naaman
@GiladNaaman: To byłaby gra o sumie zerowej, ponieważ powoduje ona odwrotny problem do tego, co ma teraz Java: równość byłaby na operatorze i musiałbyś wywołać metodę porównywania referencji. Ponadto musiałbyś nałożyć wymóg, aby wszystkie klasy implementowały coś, z czym można się związać ==. To skutecznie dodaje przeciążenie operatora, co miałoby ogromne konsekwencje dla sposobu implementacji Java.
Blrfl
1
@Blrfl: Nie bardzo. Zawsze będzie określony sposób porównywania referencji ( ClassName.ReferenceEquals(a,b)) oraz domyślny ==operator i Equalsmetoda wskazujące na ReferenceEquals.
Gilad Naaman
2

Zrobiono to inaczej w innych językach.

W Object Pascal (Delphi / Free Pascal) i C # operator równości jest zdefiniowany do porównywania wartości, a nie odniesień, podczas pracy na ciągach.

Zwłaszcza w Pascalu ciąg jest prymitywnym typem (jedna z rzeczy, które naprawdę uwielbiam w Pascalu, otrzymywanie NullreferenceException tylko z powodu niezainicjowanego ciągu jest po prostu irytujące) i mają semantykę kopiowania przy zapisie, dzięki czemu (przez większość czasu) operacje na łańcuchach są bardzo tani (innymi słowy, zauważalny dopiero po rozpoczęciu łączenia łańcuchów wielobajtowych).

Jest to więc decyzja dotycząca projektu języka Java. Kiedy projektowali język, postępowali zgodnie ze sposobem C ++ (jak Std :: String), więc łańcuchy są obiektami, co jest IMHO hackem w celu zrekompensowania C braku prawdziwego typu łańcucha, zamiast przekształcania łańcuchów w prymitywne (którymi są).

Z tego powodu mogę jedynie spekulować, że zrobili to po swojej stronie, a nie kodowanie operatora stanowi wyjątek w kompilatorze ciągów znaków.

Fabricio Araujo
źródło
Jak to odpowiada na zadane pytanie?
komara
Zobacz ostatnie zdanie (które oddzieliłem w odpowiednim akapicie w edycji).
Fabricio Araujo,
1
IMHO, Stringpowinien być prymitywnym typem w Javie. W przeciwieństwie do innych typów, kompilator musi wiedzieć o String; ponadto operacje na nim będą na tyle powszechne, że dla wielu rodzajów aplikacji mogą stanowić wąskie gardło wydajności (co można by złagodzić dzięki natywnemu wsparciu). Typowa string[mała litera] miałaby przypisany obiekt na stercie do przechowywania jego zawartości, ale nigdzie nie byłoby „normalnego” odniesienia do tego obiektu; może zatem być jednokierunkowo pośredni Char[]lub Byte[]raczej nie musi być Char[]pośredni przez inny przedmiot.
supercat
1

W Javie nie występuje przeciążanie operatorów, dlatego operatory porównania są przeciążone tylko dla typów pierwotnych.

Klasa „String” nie jest prymitywna, dlatego nie ma przeciążenia dla „==” i korzysta z domyślnego porównywania adresu obiektu w pamięci komputera.

Nie jestem pewien, ale myślę, że w Javie 7 lub 8 wyrocznia uczyniły wyjątek w kompilator rozpoznać str1 == str2jakostr1.equals(str2)

Gilad Naaman
źródło
„Nie jestem pewien, ale myślę, że w Javie 7 lub 8 wyrocznia zrobiła wyjątek w kompilatorze, aby rozpoznać str1 == str2 jako str1.equals (str2)”: Nie byłbym zaskoczony: Oracle wydaje się być mniej zaniepokojonym z minimalizmem niż Słońce.
Giorgio
2
Jeśli to prawda, to bardzo brzydki hack, ponieważ oznacza to, że jest teraz jedna klasa, którą język traktuje inaczej niż wszystkie inne i łamie kod, który porównuje odwołania. : - @
Blrfl
1
@WillihamTotland: Rozważ przeciwny przypadek. Obecnie, jeśli utworzyć dwa łańcuchy, s1a s2i dać im te same treści, przechodzą one równość ( s1.equals(s2)) porównania, ale nie samo-odniesienia ( ==) porównania, bo są dwa różne obiekty. Zmiana semantyki ==oznaczającej równość oznaczałaby s1 == s2ocenę, truegdzie była oceniana false.
Blrfl
2
@Brlfl: Chociaż to prawda, brzmi to jak wyjątkowo zła rzecz, na której można polegać, ponieważ ciągi są niezmiennymi obiektami wewnętrznymi.
Williham Totland
2
@Giorgio: „Java 7 lub 8 oracle zrobiło wyjątek w kompilatorze, aby rozpoznać str1 == str2 jako str1.equals (str2)” Nie, specyfikacja języka Java mówi: Odwołanie Operatory równości : „Jeśli operandy operatora równości są zarówno typu referencyjnego, jak i zerowego, wówczas operacją jest równość obiektu. ” To wszystko ludzie. Nie znalazłem też nic nowego na ten temat we wczesnej wersji Java 8 .
David Tonhofer
0

Wygląda na to, że Java została zaprojektowana w celu przestrzegania podstawowej zasady, że ==operator powinien być legalny za każdym razem, gdy jeden operand może zostać przekonwertowany na typ drugiego, i powinien porównywać wynik takiej konwersji z nieprzekształconym operandem.

Ta zasada nie jest unikalna dla Javy, ale ma dalekosiężne (i niefortunne) skutki w projektowaniu innych aspektów języka związanych z typem. Lepiej byłoby sprecyzować zachowania w ==odniesieniu do poszczególnych kombinacji typów operandów i zabronić kombinacji typów X i Y, gdzie x1==y1i x2==y1nie oznaczałoby to x1==x2, ale języki rzadko to robią [zgodnie z tą filozofią double1 == long1albo musiałby wskazać, czy double1nie jest dokładnym przedstawieniem long1lub odmówił skompilowania;int1==Integer1powinno być zabronione, ale powinien istnieć wygodny i skuteczny nie rzucający sposób testowania, czy obiekt jest liczbą całkowitą w pudełku o określonej wartości (porównanie z czymś, co nie jest liczbą całkowitą w ramce, powinno po prostu powrócić false)].

Jeśli chodzi o zastosowanie ==operatora do ciągów, gdyby Java zabronił bezpośrednich porównań między operandami typu Stringi Objectmógłby całkiem dobrze uniknąć niespodzianek w zachowaniu ==, ale nie ma takiego zachowania, które mógłby zastosować dla takich porównań, które nie byłyby zadziwiające. Posiadanie dwóch ciągów referencji przechowywanych w typie Objectzachowuje się inaczej niż referencje trzymane w typie Stringbyłoby znacznie mniej zadziwiające niż posiadanie któregokolwiek z tych zachowań różni się od legalnego porównania typów mieszanych. Jeśli String1==Object1jest to zgodne z prawem, oznaczałoby to, że jedynym sposobem na zachowanie String1==String2i Object1==Object2dopasowanie String1==Object1byłoby dopasowanie się.

supercat
źródło
Muszę coś przeoczyć, ale IMHO ==na obiektach powinno po prostu wywołać (null-safe) równe i coś innego (np. ===Lub System.identityEqual) powinno zostać użyte do porównania tożsamości. Mieszanie prymitywów i obiektów byłoby początkowo zabronione (przed 1.5 nie było żadnego autoboxowania), a następnie można by znaleźć prostą regułę (np. Null-bezpieczne rozpakowywanie, następnie rzutowanie, a następnie porównywanie).
maaartinus,
@maaartinus: Dobry projekt językowy powinien wykorzystywać oddzielne operatory równości dla równości wartości i odniesienia. Chociaż zgadzam się, że koncepcyjnie byłoby możliwe, aby mieć int==Integerzysk operatora false, jeśli Integerjest zerowy, a inaczej porównać wartości, podejście byłoby w przeciwieństwie do zachowania ==we wszystkich innych okolicznościach, gdzie bezwzględnie wymusza oba operandy do tego samego typu przed porównywanie ich. Osobiście zastanawiam się, czy wprowadzono automatyczne rozpakowywanie, aby umożliwić int==Integerzachowanie, które nie było nonsensowne ...
supercat,
... ponieważ automatyczne porównywanie inti porównywanie referencji byłoby głupie [ale nie zawsze by się nie udawało ]. W przeciwnym razie nie widzę powodu, aby zezwolić na niejawną konwersję, która może się nie powieść z NPE.
supercat
Myślę, że mój pomysł jest spójny. Pamiętaj tylko, że w lepszym świecie ==nie ma to nic wspólnego identityEquals. +++ „oddzielne operatory równości dla równości wartości i odniesienia” - ale które? Rozważałbym zarówno prymitywne, jak ==i porównujące wartości w tym sensie, że analizuje wartość referencji. +++ Gdy ma to na myśli , POWINIEN zrobić autoboxing i porównać referencje przy użyciu równych zeru. +++ Obawiam się, mój pomysł nie jest tak naprawdę mój, ale to, co robi Kotlin. equalsequals==equalsint==Integer
maaartinus,
@maaartinus: Gdyby ==nigdy nie testował równości odniesienia, mógłby rozsądnie wykonać zerowo bezpieczny test równości wartości. Fakt, że robi równości odniesienia testy, jednak poważnie ogranicza w jaki sposób można obsłużyć porównań mieszany odniesienia / wartość bez niekonsekwencji. Zauważ również, że Java jest ustalona w założeniu, że operatorzy promują oba operandy do tego samego typu, a nie dają specjalnych zachowań opartych na kombinacjach zaangażowanych typów. Na przykład 16777217==16777216.0fzwraca, trueponieważ wykonuje stratną konwersję pierwszego operandu na float, podczas gdy ...
supercat
0

Zasadniczo istnieje bardzo dobry powód, aby chcieć przetestować, czy dwa odwołania do obiektów wskazują na ten sam obiekt. Miałem wiele razy, które napisałem

Address oldAddress;
Address newAddress;
... populate values ...
if (oldAddress==newAddress)
... etc ...

W takich przypadkach mogę mieć lub nie mieć funkcji równości. Jeśli tak, funkcja równości może porównać całą zawartość obu obiektów. Często po prostu porównuje jakiś identyfikator. „A i B to odniesienia do tego samego obiektu”, a „A i B to dwa różne obiekty o tej samej treści” to oczywiście dwie bardzo różne idee.

Prawdopodobnie to prawda, że ​​w przypadku obiektów niezmiennych, takich jak Strings, jest to mniejszy problem. W przypadku niezmiennych obiektów mamy tendencję do myślenia o tym, że przedmiot i wartość są tym samym. Cóż, kiedy mówię „my”, mam na myśli przynajmniej „ja”.

Integer three=new Integer(3);
Integer triangle=new Integer(3);
if (three==triangle) ...

Oczywiście to zwraca fałsz, ale widzę, że ktoś uważa, że ​​to prawda.

Ale kiedy powiesz, że == porównuje uchwyty referencyjne, a nie zawartość ogólnie dla obiektów, utworzenie specjalnego przypadku dla łańcuchów byłoby potencjalnie mylące. Jak powiedział ktoś tutaj, co jeśli chcesz porównać uchwyty dwóch obiektów String? Czy byłaby jakaś specjalna funkcja, która mogłaby to zrobić tylko dla ciągów?

A co z ...

Object x=new String("foo");
Object y=new String("foo");
if (x==y) ...

Czy to fałsz, ponieważ są to dwa różne obiekty, czy prawda, ponieważ są Ciągami, których treść jest równa?

Tak, rozumiem, w jaki sposób programiści są tym zdezorientowani. Zrobiłem to sam, mam na myśli pisz, jeśli myString == „foo”, kiedy miałem na myśli, jeśli myString.equals („foo”). Ale poza przeprojektowaniem znaczenia operatora == dla wszystkich obiektów, nie wiem, jak sobie z tym poradzić.

Sójka
źródło
Zauważ, że inne współczesne języki w JVM, takie jak Scala, ==oznaczają „równe łańcuchy”.
Andres F.
@AndresF. (wzrusza ramionami) W Javie „<” oznacza „mniej niż”, podczas gdy w XML „otwiera znacznik”. W VB „=” może oznaczać, że „jest równy”, podczas gdy w Javie służy tylko do przypisania. Itd. Nic dziwnego, że różne języki używają różnych symboli dla oznaczenia tej samej rzeczy lub tego samego symbolu dla oznaczenia różnych rzeczy.
Jay
Twoja odpowiedź nie była kopaniem. To był tylko komentarz. Właśnie wskazałem, że bardziej nowoczesny język niż Java skorzystał z okazji, aby przeprojektować znaczenie ==, tak jak wspomniałeś na końcu.
Andres F.
@AndresF. A moja odpowiedź nie była kopią twojego komentarza, mówiąc tylko, że różne języki podchodzą do tych problemów na różne sposoby. :-) Naprawdę podoba mi się sposób, w jaki VB sobie z tym radzi ... pauza dla syków i buczeń z hejterów VB ... "=" zawsze porównuje wartość (dla typów zdefiniowanych przez system), czy to prymitywne, czy obiektowe. „Jest” porównuje uchwyty dwóch obiektów. Wydaje mi się to bardziej intuicyjne.
Jay
Pewnie. Ale Scala jest znacznie bliższa Javie niż Visual Basic. Lubię myśleć, że projektanci Scali zdali sobie sprawę, że użycie Javy jest ==podatne na błędy.
Andres F.
0

Jest to ważna kwestia dla Strings, a nie tylko dla strun, ale również dla innych niezmiennych obiektów reprezentujących jakąś „wartość”, np Double, BigIntegera nawet InetAddress.

Aby uczynić ==operatora użytecznym z ciągami znaków i innymi klasami wartości, widzę trzy alternatywy:

  • Niech kompilator wie o wszystkich tych klasach wartości i sposobie porównania ich zawartości. Gdyby to była tylko garść klas z java.langpakietu, zastanowiłbym się nad tym, ale to nie obejmuje przypadków takich jak InetAddress.

  • Zezwalaj na przeciążanie operatora, aby klasa definiowała ==zachowanie porównawcze.

  • Usuń konstruktory publiczne i zastosuj metody statyczne zwracające instancje z puli, zawsze zwracające tę samą instancję dla tej samej wartości. Aby uniknąć wycieków pamięci, potrzebujesz czegoś takiego jak SoftReferences w puli, który nie istniał w Javie 1.0. A teraz, aby zachować zgodność, String()konstruktorów nie można już usuwać.

Jedyne, co można dziś zrobić, to wprowadzić przeciążenie operatora, a ja osobiście nie chciałbym, aby Java poszła tą drogą.

Dla mnie najważniejsza jest czytelność kodu, a programista Java wie, że operatory mają ustalone znaczenie, określone w specyfikacji języka, podczas gdy metody są określone przez jakiś kod, a ich znaczenie należy sprawdzić w Javadoc metody. Chciałbym pozostać z tym rozróżnieniem, nawet jeśli oznacza to, że porównania ciągów nie będą mogły korzystać z ==operatora.

Irytuje mnie tylko jeden aspekt porównań Javy: efekt auto-boxowania i -unboxingu. Ukrywa różnicę między typem pierwotnym a typem opakowania. Ale gdy porównasz je ==, są one BARDZO różne.

    int i=123456;
    Integer j=123456;
    Integer k=123456;
    System.out.println(i==j);  // true or false? Do you know without reading the specs?
    System.out.println(j==k);  // true or false? Do you know without reading the specs?
Ralf Kleberhoff
źródło