Dlaczego Java nie oferuje przeciążania operatora?


406

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?



1
@zzzz, trudno mi czytać ten artykuł. Czy to było automatyczne tłumaczenie, czy też angielski jest drugim językiem pisarza? Uważam, że dyskusja tutaj jest znacznie czystsza.

25
To nagromadzenie ludzi zamykających to jako mało konstruktywne, to pytanie przyniosło jedne z najbardziej konstruktywnych dialogów, jakie widziałem w SO. Być może jest to lepszy kandydat na programmers.stackexchange.com , ale są chwile, kiedy myślę, że SO zbyt lekceważy szersze tematy.

@NoNaMe to proste, tylko psychicznie wstawić do i z - brakujące artlcles jest martwe gratisów, że dana osoba nie jest albo native speaker angielski lub programista (lub jak ten facet, zarówno programiści :) Powodem może spaść artykułów jest, że może skracaj komentarze i dopasowuj je w przewidzianym miejscu ... stamtąd po prostu przyzwyczajają się. Mój problem dotyczy układu, w jakiś sposób zawsze odwiedzam tę witrynę podczas wyszukiwania w Google. Na szczęście istnieje świetne rozszerzenie chrome o nazwie Clearly, które ponownie formatuje ciężko czytać strony cudownie.
ycomp

1
Nie widzę żadnego powodu, dlaczego i jak OP zaakceptował pierwszą odpowiedź? Odpowiedź napisana przez @ stackoverflow.com/users/14089/paercebal jest doskonała. Należy to zaakceptować.
Destructor

Odpowiedzi:


13

Zakładając, że chcesz zastąpić poprzednią wartość obiektu, o którym mowa a, funkcja wywołująca musiałaby zostać wywołana.

Complex a, b, c;
// ...
a = b.add(c);

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żytkownika Complexprzypisanie oznacza skopiowanie odwołania do istniejącej wartości.

Zastanów się zamiast tego:

b.set(1, 0); // initialize to real number '1'
a = b; 
b.set(2, 0);
assert( !a.equals(b) ); // this assertion will fail

W C ++ kopiuje to wartość, więc porównanie nie będzie równe. W języku Java, operator=wykonuje kopię odniesienia, tak ai bobecnie 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, ale operator=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=i operator==to zarówno pracy na wartościach - kopiowanie wartości i porównywanie wartości odpowiednio.


6
To naprawdę bardzo proste ... Po prostu lubię Python i nie mam przeciążonego zadania.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

225
Ta odpowiedź w ogóle nie odpowiada na pytanie. Zwracasz uwagę na użycie przez Javę znaku równości. Jeśli b + C zwróci nowy kompleks, to a = b + c byłoby całkowicie poprawne i tak, o wiele łatwiejsze do odczytania. Nawet jeśli chcesz zmodyfikować miejsce, a.set (b + c) jest toną prostszą do odczytania - szczególnie, gdy arytmetyka jest bardziej niż trywialna: a.set ((a b + b c) / 5) lub a = a.multiply (b) .add (b.multiply (c)). divide (5). Twój wybór ..
BT

24
Albo chyba nie twój wybór, w zależności od przypadku
BT

9
W C ++ szablony wyrażeń rozwiązują problem dodatkowej kopii. Prawie wszystkie główne biblioteki arytmetyczne używają tej techniki z tego właśnie powodu. Nie rozwiązuje to również pytania, ponieważ a = b + c jest po prostu cukrem syntaktycznym dla a.foo (b.bar (c)), co tak naprawdę jest początkową obserwacją w pytaniu.
Kaz Dragon,

18
To nie jest odpowiedź na zadane pytanie. To czyjeś spekulacje na temat pewnych różnic między Javą a C ++.
SChepurin,

804

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:

// C++
T operator + (const T & a, const T & b) // add ?
{
   T c ;
   c.value = a.value - b.value ; // subtract !!!
   return c ;
}

// Java
static T add (T a, T b) // add ?
{
   T c = new T() ;
   c.value = a.value - b.value ; // subtract !!!
   return c ;
}

/* C */
T add (T a, T b) /* add ? */
{
   T c ;
   c.value = a.value - b.value ; /* subtract !!! */
   return c ;
}

... Nawet w standardowych interfejsach Java

Na inny przykład zobaczmy Cloneableinterfejs 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:

class MySincereHandShake implements Cloneable
{
    public Object clone()
    {
       return new MyVengefulKickInYourHead() ;
    }
}

Ponieważ Cloneableinterfejs 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ę MyComplexNumberklasy, aby zwróciła określoną godzinę dnia. Czy toString()należy również zakazać przeciążania? Możemy sabotować, MyComplexNumber.equalsaby 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 addfunkcji, która dodaje i Cloneablemetodę 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:

// C++ comparison for built-ins and user-defined types
bool    isEqual          = A == B ;
bool    isNotEqual       = A != B ;
bool    isLesser         = A <  B ;
bool    isLesserOrEqual  = A <= B ;

// Java comparison for user-defined types
boolean isEqual          = A.equals(B) ;
boolean isNotEqual       = ! A.equals(B) ;
boolean isLesser         = A.comparesTo(B) < 0 ;
boolean isLesserOrEqual  = A.comparesTo(B) <= 0 ;

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:

// C++ container accessors, more natural
value        = myArray[25] ;         // subscript operator
value        = myVector[25] ;        // subscript operator
value        = myString[25] ;        // subscript operator
value        = myMap["25"] ;         // subscript operator
myArray[25]  = value ;               // subscript operator
myVector[25] = value ;               // subscript operator
myString[25] = value ;               // subscript operator
myMap["25"]  = value ;               // subscript operator

// Java container accessors, each one has its special notation
value        = myArray[25] ;         // subscript operator
value        = myVector.get(25) ;    // method get
value        = myString.charAt(25) ; // method charAt
value        = myMap.get("25") ;     // method get
myArray[25]  = value ;               // subscript operator
myVector.set(25, value) ;            // method set
myMap.put("25", value) ;             // method put

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 Matrixobiektu znalezionego przy użyciu pierwszych linków znalezionych w Google dla „ obiektu Java Matrix ” i „obiektu C ++ Matrix ”:

// C++ YMatrix matrix implementation on CodeProject
// http://www.codeproject.com/KB/architecture/ymatrix.aspx
// A, B, C, D, E, F are Matrix objects;
E =  A * (B / 2) ;
E += (A - B) * (C + D) ;
F =  E ;                  // deep copy of the matrix

// Java JAMA matrix implementation (seriously...)
// http://math.nist.gov/javanumerics/jama/doc/
// A, B, C, D, E, F are Matrix objects;
E = A.times(B.times(0.5)) ;
E.plusEquals(A.minus(B).times(C.plus(D))) ;
F = E.copy() ;            // deep copy of the matrix

I nie ogranicza się to do matryc. Te BigIntegeri BigDecimalklas Javy cierpieć z tego samego mylące gadatliwości, podczas gdy ich odpowiedniki w C ++ są jasne jak wbudowanych typów.

Naturalne iteratory:

// C++ Random Access iterators
++it ;                  // move to the next item
--it ;                  // move to the previous item
it += 5 ;               // move to the next 5th item (random access)
value = *it ;           // gets the value of the current item
*it = 3.1415 ;          // sets the value 3.1415 to the current item
(*it).foo() ;           // call method foo() of the current item

// Java ListIterator<E> "bi-directional" iterators
value = it.next() ;     // move to the next item & return the value
value = it.previous() ; // move to the previous item & return the value
it.set(3.1415) ;        // sets the value 3.1415 to the current item

Naturalne funktory:

// C++ Functors
myFunctorObject("Hello World", 42) ;

// Java Functors ???
myFunctorObject.execute("Hello World", 42) ;

Łączenie tekstu:

// C++ stream handling (with the << operator)
                    stringStream   << "Hello " << 25 << " World" ;
                    fileStream     << "Hello " << 25 << " World" ;
                    outputStream   << "Hello " << 25 << " World" ;
                    networkStream  << "Hello " << 25 << " World" ;
anythingThatOverloadsShiftOperator << "Hello " << 25 << " World" ;

// Java concatenation
myStringBuffer.append("Hello ").append(25).append(" World") ;

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:

// C++ primitive/advanced types
template<typename T>
T getAverage(const T & p_lhs, const T & p_rhs)
{
   return (p_lhs + p_rhs) / 2 ;
}

int     intValue     = getAverage(25, 42) ;
double  doubleValue  = getAverage(25.25, 42.42) ;
complex complexValue = getAverage(cA, cB) ; // cA, cB are complex
Matrix  matrixValue  = getAverage(mA, mB) ; // mA, mB are Matrix

// Java primitive/advanced types
// It won't really work in Java, even with generics. Sorry.

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 w addfunkcji” lub „zwracaj bzdury w clonemetodzie”).

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ł:

Pominąłem przeciążanie operatora jako dość osobisty wybór, ponieważ widziałem, jak zbyt wiele osób nadużywa go w C ++.

James Gosling. Źródło: http://www.gotw.ca/publications/c_family_interview.htm

Porównaj tekst Goslinga powyżej z tekstem Stroustrupa poniżej:

Wiele decyzji projektowych w C ++ ma swoje korzenie w mojej niechęci do zmuszania ludzi do robienia rzeczy w określony sposób [...] Często kusiło mnie, aby zakazać funkcji, których osobiście nie lubiłem, powstrzymywałem się od tego, ponieważ nie sądziłem, że miałem prawo do narzucania innym moich poglądów .

Bjarne Stroustrup. Źródło: Projektowanie i ewolucja C ++ (1.3 Tło ogólne)

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 structa structwywodzi 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 ...


50
To doskonała odpowiedź. Nie zgadzam się z tym, ale wciąż jest to doskonała odpowiedź. Myślę, że problemy, które są możliwe przy złych przeciążeniach, przekraczają wartość dobrych przeciążeń.
Douglas Leeder,

69
@Douglas Leeder: Dzięki! Przeciążenie operatora jest jak OOP. Gdy uczysz się tego po raz pierwszy, piszesz przeciążenia wszędzie tak, jak wszędzie umieszczasz klasy podstawowe i dziedziczenie (jak słodka ironia, Java API). Ale to mija dość szybko i wtedy doceniasz tę możliwość, nie nadużywając jej. Moje własne 10-letnie doświadczenie z C ++ polega na tym, że liczba złych przeciążeń, które widziałem zarówno w moim kodzie, jak i w kodzie innych programistów, jest tak niska, że ​​wierzę, że mógłbym je policzyć z jednej strony. Jest to o wiele mniej niż ogólna liczba błędów związanych z przepełnieniem sprintf, strcat, memset i bufora.
paercebal,

11
@Douglas Leeder: Po dyskusji na ten temat w innym pytaniu SO wierzę, że luka między „kochankami” a „nienawidzącymi” przeciążenia operatora jest prawdopodobnie spowodowana różnicą w podejściu do kodu: „hejterzy są więcej” funkcji są ważne ”, co oznacza, że ​​oczekują, że funkcja wykona jedną rzecz i tylko jedną rzecz. Dlatego operatorzy powinni działać zgodnie z projektem języka. „Kochankowie” bardziej dotyczą „obiektów powinny się zachowywać”, co oznacza, że ​​łatwiej akceptują tę funkcję (a zatem operatorów) mogą zmieniać swoje zachowanie zgodnie z rodzajem swoich parametrów.
paercebal,

103
Epicka odpowiedź. Jeden z najbardziej wykwalifikowanych debunków, jakie kiedykolwiek czytałem.
Sebastian Mach

7
@ MaartenBodewes: Wszystkie przykłady, które napisałem powyżej, a jedyne, co Ci przeszkadza, to „jako programista, jesteś pieprzony, ponieważ osobisty wybór Goslinga” ? Proszę, napisz własną odpowiedź, broniąc kąta „wy, programiści, jesteście głupi, pozwólcie genialnym ludziom decydować za was, czego potrzebujecie” . Ta dyskusja nie ma sensu.
paercebal 17.09.16

44

James Gosling porównał projektowanie Javy do następujących:

„W przypadku przeprowadzki z jednego mieszkania do drugiego obowiązuje ta zasada. Ciekawym eksperymentem jest spakowanie mieszkania i umieszczenie wszystkiego w pudełkach, a następnie przejście do następnego mieszkania i nie rozpakowywanie niczego, dopóki nie będzie to potrzebne.” przygotowujesz swój pierwszy posiłek, a ty wyciągasz coś z pudełka. Potem po około miesiącu wykorzystałeś to, by właściwie dowiedzieć się, czego naprawdę potrzebujesz w życiu, a potem resztę rzeczy - zapomnij, jak ci się podoba lub jak fajnie - i po prostu je wyrzucisz. To niesamowite, jak to upraszcza życie, i możesz zastosować tę zasadę we wszelkiego rodzaju kwestiach projektowych: nie rób rzeczy tylko dlatego, że są fajne lub po prostu dlatego, że są interesujące. ”

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 ++ .


6
Czy możesz podać kodowy przykład „złożoności przeciążenia operatora w połączeniu z wartością graniczną i wyjątkami”? Pomimo kilku lat zabawy z tym językiem, posiadania i przeczytania wszystkich skutecznych / wyjątkowych książek na temat C ++, nie rozumiem, co masz na myśli.
paercebal

60
To, co działa dla Jamesa Goslinga, nie będzie działać dla wszystkich. Jest niewiarygodnie krótkowzroczny za ekstrapolację swojego „interesującego” eksperymentu pakowania, co oznacza „Wyrzuć wszystko na świecie, czego nie potrzebuję, więc nikt nie może tego użyć”. Najwyraźniej nie wie, czego potrzebuję.
BT

49
@BT: Większość enlightning jest punkt widzenia Gosling w porównaniu z punktu widzenia Stroustrupa w tej kwestii: 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).
paercebal

29
@ Software Monkey: „C ++, powszechnie oczerniany w porównaniu do innych, Java, powszechnie lubiany” To marketingowy szum. Pamiętaj, że C ++ rozwijał się sam, podczas gdy Java (i .NET) korzystały z buldożerów marketingowych. Czy nie wydaje się dziwne, że dla „powszechnie lubianego języka” Java jest ograniczona do aplikacji serwerowych, podczas gdy „powszechnie oczerniana” (prawdopodobnie przez programistów i menedżerów Javy chcących obniżyć koszty produkcji kodu) C ++ pochodzi od bardzo wysokich serwery wydajności do gier o wysokiej wydajności? [...]
paercebal

16
@Hassan: Każdy język ma swoje włamania, a ogólna Java jest doskonałym tego przykładem. Teraz o 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 ++.
paercebal

22

Sprawdź Boost.Units: link tekst

Zapewnia zerową analizę wymiarową poprzez przeciążenie operatora. O ile jaśniej to można uzyskać?

quantity<force>     F = 2.0*newton;
quantity<length>    dx = 2.0*meter;
quantity<energy>    E = F * dx;
std::cout << "Energy = " << E << endl;

wyprowadziłoby „Energię = 4 J”, co jest poprawne.


1
„Jak dokładnie, jeśli komplikuje konserwację i gdzie, u licha, ten kod jest zaciemniony?”
Mooing Duck

13

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.Equalsi Object.ReferenceEquals(lub jakkolwiek to się nazywa).


8

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)”.


4
Chciałbym, żeby wszystkie języki z dużą składnią i przeciążeniem operatora używałyby tej techniki. Nigdy nie zrozumiałem, dlaczego muszą wymyślić specjalną wersję nazewnictwa i wyszukiwania metod. Stroustrup nie wspomina o żadnych alternatywach w D & EC ++. Zespół C # przyjął właściwe podejście ze składnią Linq ( 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).
Daniel Earwicker

@DanielEarwicker, często zauważyłem, że kiedy dochodzi do skomplikowanych sporów, ludzie określają motywacje obu stron jako „religijne” z natury.

@ noah, mógłbym żyć z ograniczonym podziałem przeciążania operatora, takim jak ten, pod warunkiem, że istnieje specjalny znacznik w nazwach metod, które utrzymują je wizualnie odrębne. Coś jak zdefiniowanie metody __plus () dla implementacji OL „+” i trzymanie się z daleka przeciążając rzeczy takie jak rzutowania, a nawet indeksy tablicowe. To, z czym nie chcę żyć, to sposób, w jaki C ++ i C # uznały za stosowne do jego wdrożenia.

2
Nie odpowiedź. Na maszynie wirtualnej działa wiele języków. Przeciążenie operatora nie powinno być samo w sobie dobrym powodem do zmiany języka.
Maarten Bodewes,

6

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.


14
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 *,
rzutowania

6
-1. Każda przypisana zmienna jest symbolem, podobnie jak symbole operatora arytmetycznego. To, czy użyjesz frazy do nazwania tej zmiennej, pojedynczego słowa lub pojedynczej litery, jest decyzją (lub twojego zespołu). Kto ma powiedzieć, co jest znaczące, a co nie? Odpowiedź brzmi: ty, programista. W czystej matematyce mnożenie między macierzami oznacza coś innego niż mnożenie dwóch liczb w podstawowej arytmetyce. Jednak używamy tych samych symboli dla obu rodzajów mnożenia.
Inżynier

2
@paercebal: twierdzenie jest niestety poprawne. Nie musisz szukać dalej niż IOstreams, aby zobaczyć, jak działa. Na szczęście większość programistów jest bardziej ostrożna w opracowywaniu nowej semantyki dla istniejących operatorów.
Ben Voigt

5
@BenVoigt: [...] I nawet nie wspominam o tym, że ta addfunkcja 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.
paercebal

3
@ jbo5112: Przykład: Nawiasy nie wyjaśniają cout << f() || g(); , lecz poprawiają. A gdyby operatorzy zmiany bitów nie byli nadużywani, nie byliby potrzebni. Dlaczego jest cout << (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.
Ben Voigt,

5

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. :)


Jak na przykład ten głupi błąd ...
Amir Kirsh

2
To bardzo zły sposób myślenia. Możesz strzelić sobie w stopę, raczej ścięliśmy ci ręce, więc nie będziesz w stanie. I oczywiście przypuszczamy, że jesteś idiotą, który się zastrzelił.
ntj

5

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).


4

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.


4

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.


Używanie przez Javę +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 na Real, choć wyniki, które mogą nie być liczbami całkowitymi, muszą zostać podane przez Trunclub Roundprzed przypisaniem liczb całkowitych.
supercat

2

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.

Nie jest:

Complex a, b, c; a = b + c;

o wiele prostsze niż:

Complex a, b, c; a = b.add(c);

2
Jestem? ;) Równe mogą oznaczać zarówno przypisanie, jak i porównanie, ale = oznacza zawsze przypisanie, a == to zawsze porównanie. Nazwy mogą same wprowadzać duże źródła błędów.

1

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.

// a = b + c
Complex a, b, c; a = b.add(c);

12
Oczywiście, jak wspomniano w innym miejscu, nigdy nie można być pewnym znaczenia funkcji add.
Zaćmienie

To prawda, że ​​wciąż mi się podoba fakt, że przynajmniej moi operatorzy są zakodowani na stałe. Oczywiście posiadanie tych funkcji i rozsądne korzystanie z nich przyniosłoby nam tylko dobro. Problem polega na tym, że trudno jest stwierdzić, czy ktoś rozsądnie z nich korzystał. I że zgadzasz się na definicję rozsądku. :-)

1
Komentarz dodany w celu wyjaśnienia kodu jest taki, jak wyglądałby kod w języku, który obsługiwał przeciążanie operatora. Co więcej, fakt, że komentarz został napisany w kategoriach operatorów, przeczy twojemu sprzeciwowi wobec przeciążenia operatora.
Aluan Haddad,

0

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:

>>> IP()
<IP |>
>>> IP()/TCP()
<IP frag=0 proto=TCP |<TCP |>>
>>> Ether()/IP()/TCP()
<Ether type=0x800 |<IP frag=0 proto=TCP |<TCP |>>>
>>> IP()/TCP()/"GET / HTTP/1.0\r\n\r\n"
<IP frag=0 proto=TCP |<TCP |<Raw load='GET / HTTP/1.0\r\n\r\n' |>>>
>>> Ether()/IP()/IP()/UDP()
<Ether type=0x800 |<IP frag=0 proto=IP |<IP frag=0 proto=UDP |<UDP |>>>>
>>> IP(proto=55)/TCP()
<IP frag=0 proto=55 |<TCP |>>

Oto wyjaśnienie:

Operator / został użyty jako operator kompozycji między dwiema warstwami. W ten sposób w dolnej warstwie może zostać przeciążone jedno lub więcej domyślnych pól zgodnie z górną warstwą. (Nadal możesz podać żądaną wartość). Ciąg może być używany jako surowa warstwa.


0

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ć:

  1. Użyj innego języka. Zarówno Groovy, jak i Scala mają przeciążenie operatora i są oparte na Javie.
  2. Użyj java-oo , wtyczki, która umożliwia przeciążanie operatora w Javie. Pamiętaj, że NIE jest on niezależny od platformy. Ma też wiele problemów i nie jest kompatybilny z najnowszymi wersjami Java (tj. Java 10). ( Oryginalne źródło StackOverflow )
  3. Użyj JNI , Java Native Interface lub alternatyw. To pozwala pisać metody C lub C ++ (może inne?) Do użycia w Javie. Oczywiście NIE jest to również niezależne od platformy.

Jeśli ktoś jest świadomy innych, proszę o komentarz, a ja dodam go do tej listy.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.