Przechodząc od C ++ do Java, oczywistym pytaniem bez odpowiedzi jest to, dlaczego Java nie zawierała przeciążenia operatora?
Czy to nie Complex a, b, c; a = b + c;
jest prostsze niż Complex a, b, c; a = b.add(c);
?
Czy jest znany znany powód, aby nie dopuszczać do przeciążenia operatora? Czy powód jest arbitralny, czy może stracony w czasie?
java
operator-overloading
rengoliny
źródło
źródło
Odpowiedzi:
Zakładając, że chcesz zastąpić poprzednią wartość obiektu, o którym mowa
a
, funkcja wywołująca musiałaby zostać wywołana.W C ++ to wyrażenie mówi kompilatorowi, aby utworzył trzy (3) obiekty na stosie, wykonał dodawanie i skopiował wynikową wartość z obiektu tymczasowego do istniejącego obiektu
a
.Jednak w Javie
operator=
nie wykonuje kopiowania wartości dla typów referencyjnych, a użytkownicy mogą tworzyć tylko nowe typy referencyjne, a nie typy wartości. Tak więc w przypadku typu zdefiniowanego przez użytkownikaComplex
przypisanie oznacza skopiowanie odwołania do istniejącej wartości.Zastanów się zamiast tego:
W C ++ kopiuje to wartość, więc porównanie nie będzie równe. W języku Java,
operator=
wykonuje kopię odniesienia, taka
ib
obecnie odnosi się do tej samej wartości. W rezultacie porównanie da „równy”, ponieważ obiekt porówna się z samym sobą.Różnica między kopiami a odniesieniami tylko pogmatwa przeciążenie operatora. Jak wspomniano w @Sebastian, zarówno Java, jak i C # muszą osobno
operator+
radzić sobie z równością wartości i referencji - prawdopodobnie poradziłyby sobie z wartościami i obiektami, aleoperator=
są już zaimplementowane do obsługi referencji.W C ++ powinieneś mieć do czynienia tylko z jednym rodzajem porównania na raz, więc może być mniej mylące. Na przykład, na
Complex
,operator=
ioperator==
to zarówno pracy na wartościach - kopiowanie wartości i porównywanie wartości odpowiednio.źródło
Wiele postów narzeka na przeciążenie operatora.
Czułem, że muszę wyjaśnić pojęcia „przeciążania operatora”, oferując alternatywny punkt widzenia na tę koncepcję.
Kod zaciemnia?
Ten argument jest błędem.
Zaciemnianie jest możliwe we wszystkich językach ...
Kod w C lub Javie jest równie łatwy do zaciemnienia za pomocą funkcji / metod, jak w C ++ przez przeciążenia operatora:
... Nawet w standardowych interfejsach Java
Na inny przykład zobaczmy
Cloneable
interfejs w Javie:Powinieneś sklonować obiekt implementujący ten interfejs. Ale możesz kłamać. I stwórz inny obiekt. W rzeczywistości ten interfejs jest tak słaby, że możesz zwrócić inny typ obiektu, dla zabawy:
Ponieważ
Cloneable
interfejs może być nadużywany / zaciemniany, czy powinien być zbanowany z tego samego powodu, co powinno być przeciążanie operatora C ++?Możemy przeciążyć
toString()
metodęMyComplexNumber
klasy, aby zwróciła określoną godzinę dnia. CzytoString()
należy również zakazać przeciążania? Możemy sabotować,MyComplexNumber.equals
aby zwrócił losową wartość, zmodyfikował operandy ... itd. Itd. Itd.W Javie, podobnie jak w C ++ lub w dowolnym innym języku, programista musi przestrzegać minimum semantyki podczas pisania kodu. Oznacza to zaimplementowanie
add
funkcji, która dodaje iCloneable
metodę implementacji, która klonuje, oraz++
operatora niż inkrementy.Co i tak zaciemnia?
Teraz, gdy wiemy, że kod może być sabotowany nawet przy użyciu nieskazitelnych metod Java, możemy zadać sobie pytanie o faktyczne wykorzystanie przeciążenia operatora w C ++?
Jasny i naturalny zapis: metody a przeciążenie operatora?
Porównamy poniżej, dla różnych przypadków, „ten sam” kod w Javie i C ++, aby zorientować się, który styl kodowania jest wyraźniejszy.
Naturalne porównania:
Należy pamiętać, że A i B mogą być dowolnego typu w C ++, o ile występują przeciążenia operatora. W Javie, gdy A i B nie są prymitywami, kod może stać się bardzo mylący, nawet w przypadku obiektów podobnych do prymitywów (BigInteger itp.) ...
Naturalne akcesoria tablic / kontenerów i indeksowanie:
W Javie widzimy, że dla każdego kontenera, który robi to samo (dostęp do jego zawartości poprzez indeks lub identyfikator), mamy inny sposób, aby to zrobić, co jest mylące.
W C ++ każdy kontener używa tego samego sposobu dostępu do swojej zawartości, dzięki przeciążeniu operatora.
Naturalna manipulacja typami zaawansowanymi
W poniższych przykładach użyto
Matrix
obiektu znalezionego przy użyciu pierwszych linków znalezionych w Google dla „ obiektu Java Matrix ” i „obiektu C ++ Matrix ”:I nie ogranicza się to do matryc. Te
BigInteger
iBigDecimal
klas Javy cierpieć z tego samego mylące gadatliwości, podczas gdy ich odpowiedniki w C ++ są jasne jak wbudowanych typów.Naturalne iteratory:
Naturalne funktory:
Łączenie tekstu:
Ok, w Javie też możesz użyć
MyString = "Hello " + 25 + " World" ;
... Ale poczekaj chwilę: to przeciążenie operatora, prawda? Czy to nie oszustwo?:-RE
Kod ogólny?
Te same ogólne operandy modyfikujące kod powinny być użyteczne zarówno dla wbudowanych / prymitywnych (które nie mają interfejsów w Javie), standardowych obiektów (które nie mogą mieć odpowiedniego interfejsu), jak i obiektów zdefiniowanych przez użytkownika.
Na przykład obliczanie średniej wartości dwóch wartości dowolnych typów:
Omówienie przeciążenia operatora
Teraz, gdy widzieliśmy uczciwe porównania między kodem C ++ używającym przeciążenia operatora, a tym samym kodem w Javie, możemy teraz omówić „przeciążenie operatora” jako koncepcję.
Przeciążenie operatora istniało jeszcze przed komputerami
Nawet poza informatyki, jest przeciążanie operatorów: Na przykład w matematyce, jak operatorzy
+
,-
,*
, itd. Są przeciążone.Rzeczywiście, istotności
+
,-
,*
itp zmienia się w zależności od typów argumentów (numeryczne, wektory, funkcji fali kwantowej, macierze, itp.);Większość z nas, w ramach kursów naukowych, nauczyła się wielu znaczeń dla operatorów, w zależności od rodzaju operandów. Czy stwierdziliśmy, że są mylące?
Przeciążenie operatora zależy od jego argumentów
Jest to najważniejsza część przeciążania operatora: Podobnie jak w matematyce lub fizyce, operacja zależy od rodzaju operandów.
Więc poznaj typ operandu, a poznasz efekt operacji.
Nawet C i Java mają (zakodowane na stałe) przeciążenie operatora
W C rzeczywiste zachowanie operatora zmieni się zgodnie z operandami. Na przykład dodanie dwóch liczb całkowitych różni się od dodania dwóch liczb podwójnych lub nawet jednej liczby całkowitej i jednej liczby podwójnej. Istnieje nawet cała domena arytmetyczna wskaźnika (bez rzutowania można dodać do wskaźnika liczbę całkowitą, ale nie można dodać dwóch wskaźników ...).
W Javie nie ma arytmetyki wskaźników, ale ktoś nadal stwierdził, że konkatenacja ciągów bez
+
operatora byłaby na tyle absurdalna, aby uzasadnić wyjątek w wyznaniu „przeciążanie operatora jest złe”.Tyle, że jako programista C (z powodów historycznych) lub Java (z powodów osobistych , patrz poniżej) nie możesz podać własnego.
W C ++ przeciążenie operatora nie jest opcjonalne ...
W C ++ przeciążanie operatorów dla typów wbudowanych nie jest możliwe (i jest to dobra rzecz), ale typy zdefiniowane przez użytkownika mogą mieć przeciążenia operatora zdefiniowane przez użytkownika .
Jak już powiedziano wcześniej, w C ++ i w przeciwieństwie do Javy typy użytkowników nie są uważane za obywateli drugiej kategorii języka, w porównaniu do typów wbudowanych. Tak więc, jeśli typy wbudowane mają operatory, typy użytkowników również powinny je mieć.
Prawda jest taka, że, podobnie jak
toString()
,clone()
,equals()
metody są dla Javy ( czyli quasi-norma podobny ), C ++ przeciążenie operatora jest tak samo częścią C ++, który staje się tak naturalne jak oryginalnych operatorów C lub przed wymienionych metod Javy.W połączeniu z programowaniem szablonów przeciążenie operatora staje się dobrze znanym wzorcem projektowym. W rzeczywistości nie można posunąć się bardzo daleko w STL bez użycia przeciążonych operatorów i przeciążających operatorów dla własnej klasy.
... ale nie należy go nadużywać
Przeciążenie operatora powinno dążyć do przestrzegania semantyki operatora. Nie odejmuj w
+
operatorze (jak w „nie odejmuj wadd
funkcji” lub „zwracaj bzdury wclone
metodzie”).Przeciążenie rzutów może być bardzo niebezpieczne, ponieważ może prowadzić do niejednoznaczności. Dlatego powinny być naprawdę zarezerwowane dla ściśle określonych przypadków. Jak dla
&&
i||
, nigdy nie przeciążać ich, chyba że naprawdę wiesz co robisz, jak stracisz oceny zwarcie że rodzimych operatorów&&
i||
cieszyć się.Więc ... Ok ... Więc dlaczego nie jest to możliwe w Javie?
Ponieważ James Gosling powiedział:
Porównaj tekst Goslinga powyżej z tekstem Stroustrupa poniżej:
Czy przeciążenie operatora przyniosłoby korzyści w Javie?
Niektóre obiekty znacznie skorzystałyby na przeciążeniu operatora (typy betonowe lub numeryczne, takie jak BigDecimal, liczby zespolone, macierze, kontenery, iteratory, komparatory, parsery itp.).
W C ++ możesz skorzystać z tej korzyści dzięki pokorze Stroustrupa. W Javie jesteś po prostu pieprzony z powodu osobistego wyboru Goslinga .
Czy można go dodać do Javy?
Przyczyny braku dodawania przeciążenia operatora teraz w Javie może być mieszanką wewnętrznej polityki, alergii na tę funkcję, nieufności do programistów (wiesz, sabotażyści, którzy wydają się prześladować zespoły Java ...), kompatybilności z poprzednimi maszynami JVM, czas na napisanie poprawnej specyfikacji itp.
Więc nie wstrzymuj oddechu, czekając na tę funkcję ...
Ale robią to w C # !!!
Tak...
Chociaż nie jest to jedyna różnica między tymi dwoma językami, ten nigdy mnie nie bawi.
Najwyraźniej ludzie z C #, z ich „każdym prymitywem jest
struct
astruct
wywodzi się od Object” , mieli to za pierwszym razem.I robią to w innych językach !!!
Pomimo wszystkich FUD przeciwko używanemu zdefiniowanemu przeciążeniu operatora, obsługiwane są następujące języki: Scala , Dart , Python , F # , C # , D , Algol 68 , Smalltalk , Groovy , Perl 6 , C ++, Ruby , Haskell , MATLAB , Eiffel , Lua , Clojure , Fortran 90 , Swift , Ada , Delphi 2005 ...
Tak wiele języków, z tak wieloma różnymi (a czasem sprzecznymi) filozofiami, a jednak wszyscy są zgodni co do tego.
Jedzenie do przemyślenia ...
źródło
James Gosling porównał projektowanie Javy do następujących:
Tutaj możesz przeczytać kontekst cytatu
Zasadniczo przeciążenie operatora jest idealne dla klasy, która modeluje jakiś punkt, walutę lub liczbę zespoloną. Ale potem szybko zaczyna brakować przykładów.
Innym czynnikiem było nadużywanie tej funkcji w C ++ przez programistów przeciążających operatorów takich jak „&&”, „||”, operatory rzutowania i oczywiście „nowe”. Złożoność wynikająca z połączenia tego z przekazywaniem wartości i wyjątkami jest dobrze opisana w książce Wyjątkowa C ++ .
źródło
Many C++ design decisions have their roots in my dislike for forcing people to do things in some particular way [...] Often, I was tempted to outlaw a feature I personally disliked, I refrained from doing so because I did not think I had the right to force my views on others. (B. Stroustrup)
.I'd like them to go have a look at some C++ code out there that is hideously put together with weird hacks and "exceptional" features of the language
: Bad programiści będą pisać zły kod bez względu na język. Po prostu spróbuj emulować „pass-by-reference” dla parametrów funkcji w Javie, aby mieć pomysł. Widziałem kod i śmiałem się tak mocno, że bolało. Tego rodzaju rzeczy Gosling nie używał, dlatego potrzebował strasznych hacków w Javie, ale istnieje natywnie, przy zerowym koszcie, zarówno w C #, jak i C ++.Sprawdź Boost.Units: link tekst
Zapewnia zerową analizę wymiarową poprzez przeciążenie operatora. O ile jaśniej to można uzyskać?
wyprowadziłoby „Energię = 4 J”, co jest poprawne.
źródło
Projektanci Java zdecydowali, że przeciążenie operatora było większym problemem niż było warte. Proste.
W języku, w którym każda zmienna obiektowa jest w rzeczywistości odniesieniem, przeciążenie operatora stwarza dodatkowe ryzyko bycia nielogicznym - przynajmniej dla programisty C ++. Porównaj sytuację z przeciążeniem operatora C # ==
Object.Equals
iObject.ReferenceEquals
(lub jakkolwiek to się nazywa).źródło
Groovy ma przeciążenie operatora i działa w JVM. Jeśli nie przeszkadza ci hit wydajności (który z każdym dniem zmniejsza się). Jest to automatyczne na podstawie nazw metod. np. „+” wywołuje metodę „plus (argument)”.
źródło
where ...
staje się.Where(i => ...
). Gdyby tylko zrobili to samo z operatorami arytmetycznymi, tak wiele rzeczy byłoby prostszych i potężniejszych. Java ma tę zaletę, że ma czyste konto i może to zrobić poprawnie (choć z powodów religijnych prawdopodobnie nigdy nie będzie).Myślę, że był to świadomy wybór projektowy, zmuszający programistów do tworzenia funkcji, których nazwy jasno wyrażają ich intencje. W C ++ programiści przeciążaliby operatorów funkcjami, które często nie miałyby związku z powszechnie akceptowanym charakterem danego operatora, przez co prawie niemożliwe było ustalenie, co robi kawałek kodu bez patrzenia na definicję operatora.
źródło
In C++ developers would overload operators with functionality that would often have no relation to the commonly accepted nature of the given operator
: To nieuzasadnione twierdzenie. Jestem profesjonalnym programistą C ++ od 12 lat i rzadko napotykałem ten problem. W rzeczywistości najwięcej błędów i błędów projektowych, które widziałem w C ++, było w kodzie w stylu C (void *
,add
funkcja może być naprawdę źle użyta (jak mnożenie lub nabywanie muteksu) ... Nadużycie wspomniane przez user14128 nie ogranicza się do operatorów, ale istnieje pewien patologiczny strach przed przeciążeniem operatora, który, jak sądzę, pochodzi z wcześniejszych dni C vs. C ++, strach, który przeszedł niezmodyfikowany bezpośrednio do Javy, ale na szczęście nie wszedł w C # ... W końcu, szanując semantykę a pisanie jasnych funkcji / operatorów jest zadaniem programisty. Nie w języku.cout << f() || g();
, lecz poprawiają. A gdyby operatorzy zmiany bitów nie byli nadużywani, nie byliby potrzebni. Dlaczego jestcout << (5&3) << endl;
lepszy niżcout.fmt(5&3)(endl);
? Użycie operatora wywołania funkcji w zmiennej elementu funktora byłoby nieskończenie lepszą konstrukcją dla strumieni niż ponowne użycie operatorów bitowych tylko dlatego, że glif wygląda ładnie. Ale nie jest to jedyne złe zjawisko w przypadku strumieni.Możesz przecież naprawdę zastrzelić się w stopę z przeciążeniem operatora. To tak, jakby ludzie popełniali ze sobą głupie błędy, dlatego postanowiono zabrać nożyczki.
Przynajmniej myślę, że to jest powód. I tak jestem po twojej stronie. :)
źródło
Niektórzy twierdzą, że przeciążenie operatora w Javie doprowadziłoby do zaciemnienia. Czy ci ludzie kiedykolwiek przestali patrzeć na jakiś kod Java wykonujący podstawowe matematyki, takie jak zwiększenie wartości finansowej o procent za pomocą BigDecimal? ... gadatliwość takiego ćwiczenia staje się własnym dowodem zaciemnienia. Jak na ironię, dodanie przeciążenia operatora do Javy pozwoliłoby nam stworzyć własną klasę walut, która uczyniłaby taki kod matematyczny eleganckim i prostym (mniej zaciemnionym).
źródło
Mówiąc, że przeciążenie operatora prowadzi do błędów logicznych typu, że operator nie pasuje do logiki operacji, to tak, jakby nic nie mówić. Ten sam typ błędu wystąpi, jeśli nazwa funkcji jest nieodpowiednia dla logiki operacji - więc jakie jest rozwiązanie: porzuć możliwość korzystania z funkcji !? To jest komiczna odpowiedź - „Nieodpowiednie do logiki operacji”, każda nazwa parametru, każda klasa, funkcja lub cokolwiek innego, co może być logicznie nieodpowiednie. Myślę, że ta opcja powinna być dostępna w szanowanym języku programowania i dla tych, którzy uważają, że jest niebezpieczny - hej, oboje nie mówią, że musisz z niego korzystać. Weźmy C #. Opuścili wskaźniki, ale hej - istnieje „niebezpieczny kod” - program, jak chcesz na własne ryzyko.
źródło
Technicznie istnieje przeciążenie operatora w każdym języku programowania, który może obsługiwać różne typy liczb, np. Liczby całkowite i rzeczywiste. Objaśnienie: Termin przeciążenie oznacza, że dla jednej funkcji jest po prostu kilka implementacji. W większości języków programowania dostępne są różne implementacje dla operatora +, jedna dla liczb całkowitych, jedna dla liczb rzeczywistych, nazywa się to przeciążeniem operatora.
Teraz wiele osób uważa za dziwne, że Java ma przeciążenie operatora dla operatora + za dodawanie ciągów razem, i z matematycznego punktu widzenia byłoby to rzeczywiście dziwne, ale z punktu widzenia dewelopera języka programowania nie ma nic złego w dodawaniu przeciążenia wbudowanego operatora dla operatora + dla innych klas np. String. Jednak większość ludzi zgadza się, że po dodaniu wbudowanego przeciążenia dla + dla String, dobrym pomysłem jest zapewnienie tej funkcji również programistom.
Całkowicie nie zgadzam się z błędem, że przeciążenie operatora zaciemnia kod, ponieważ programista decyduje o tym. To naiwne myśleć i, szczerze mówiąc, się starzeje.
+1 za dodanie przeciążenia operatora w Javie 8.
źródło
+
do łączenia dowolnych ciągów znaków jest dość ohydne przez IMHO, podobnie jak przeciążanie/
w C i FORTRAN dla całego i ułamkowego podziału. W wielu wersjach Pascala użycie operatorów arytmetycznych na dowolnym typie liczbowym da wyniki liczbowo równoważne rzutowaniu operandów naReal
, choć wyniki, które mogą nie być liczbami całkowitymi, muszą zostać podane przezTrunc
lubRound
przed przypisaniem liczb całkowitych.Zakładając, że Java jako język implementacji to a, b i c byłyby odwołaniami do typu Complex z początkowymi wartościami null. Zakładając również, że Complex jest niezmienny jako wspomniany BigInteger i podobny niezmienny BigDecimal , myślę, że masz na myśli następujące, ponieważ przypisujesz odwołanie do Complex zwróconego po dodaniu b i c, a nie porównując tego odniesienia do a.
źródło
Czasami byłoby miło mieć przeciążenie operatora, klasy znajomych i wielokrotne dziedziczenie.
Jednak nadal uważam, że była to dobra decyzja. Gdyby Java miałaby przeciążenie operatora, nigdy nie moglibyśmy być pewni znaczenia operatora bez przeglądania kodu źródłowego. Obecnie nie jest to konieczne. I myślę, że twój przykład użycia metod zamiast przeciążania operatora jest również dość czytelny. Jeśli chcesz, aby wszystko było bardziej jasne, zawsze możesz dodać komentarz powyżej owłosionych wypowiedzi.
źródło
Nie jest to dobry powód, aby to zabronić, ale praktyczny:
Ludzie nie zawsze używają go odpowiedzialnie. Spójrz na ten przykład ze skryptu biblioteki Python:
Oto wyjaśnienie:
źródło
Alternatywy dla macierzystej obsługi przeciążania operatora Java
Ponieważ Java nie ma przeciążenia operatora, oto kilka alternatyw, na które możesz spojrzeć:
Jeśli ktoś jest świadomy innych, proszę o komentarz, a ja dodam go do tej listy.
źródło
Podczas gdy język Java nie obsługuje bezpośrednio przeciążania operatora, można go włączyć w dowolnym kompilatorze Java w kompilatorze kompilatora Manifold . Obsługuje Java 8 - 13 (aktualna wersja Java) i jest w pełni obsługiwany w IntelliJ IDEA.
źródło