Perché Java non offre sovraccarico all'operatore?


406

Venendo dal C ++ a Java, l'ovvia domanda senza risposta è perché Java non includeva il sovraccarico dell'operatore?

Non è Complex a, b, c; a = b + c;molto più semplice di Complex a, b, c; a = b.add(c);?

C'è un motivo noto per questo, validi argomenti per non consentire il sovraccarico dell'operatore? Il motivo è arbitrario o perduto nel tempo?



1
@zzzz, faccio fatica a leggere quell'articolo. Questo è stato tradotto automaticamente, o l'inglese è la seconda lingua dello scrittore? Trovo che la discussione qui sia molto più pulita.

25
Per il cumulo di persone che ha chiuso questo come non costruttivo, questa domanda ha prodotto alcuni dei dialoghi più costruttivi che ho visto a SO. Forse è un candidato migliore per programmers.stackexchange.com , ma ci sono momenti in cui penso che SO sia eccessivamente sprezzante per argomenti più ampi.

@NoNaMe è facile, basta inserire mentalmente un e il - artlcles mancante è un omaggio morto che la persona o non è un madrelingua inglese o un programmatore (o come questo ragazzo, entrambi :) Il motivo per cui i programmatori possono rilasciare articoli è che può rendere i commenti più brevi e adattarli più facilmente nello spazio fornito .. da lì, si sono semplicemente abituati. Il mio problema è con il layout, in qualche modo colpisco sempre quel sito nelle ricerche di Google. Fortunatamente c'è una grande estensione di Chrome chiamata Clearly che riformatta le pagine difficili da leggere meravigliosamente.
ycomp,

1
Non vedo alcun motivo per cui e come OP ha accettato la prima risposta? La risposta scritta da @ stackoverflow.com/users/14089/paercebal è eccellente. Dovrebbe essere accettato.
Distruttore,

Risposte:


13

Supponendo che si desideri sovrascrivere il valore precedente dell'oggetto a cui fa riferimento a, si dovrebbe invocare una funzione membro.

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

In C ++, questa espressione dice al compilatore di creare tre (3) oggetti nello stack, eseguire addizioni e copiare il valore risultante dall'oggetto temporaneo nell'oggetto esistente a.

Tuttavia, in Java, operator=non esegue la copia del valore per i tipi di riferimento e gli utenti possono solo creare nuovi tipi di riferimento, non tipi di valore. Pertanto, per un tipo definito dall'utente denominato Complex, assegnazione significa copiare un riferimento a un valore esistente.

Considera invece:

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

In C ++, questo copia il valore, quindi il confronto risulterà diverso. In Java, operator=esegue la copia di riferimento, quindi ae bora si riferiscono allo stesso valore. Di conseguenza, il confronto produrrà "uguale", poiché l'oggetto comparerà uguale a se stesso.

La differenza tra copie e riferimenti non fa che aumentare la confusione del sovraccarico dell'operatore. Come accennato da @Sebastian, Java e C # devono entrambi trattare separatamente l'uguaglianza di valore e di riferimento - operator+probabilmente tratteranno di valori e oggetti, ma operator=è già implementato per gestire i riferimenti.

In C ++, dovresti avere a che fare solo con un tipo di confronto alla volta, quindi può essere meno confuso. Ad esempio, su Complex, operator=e operator==sono entrambi a lavorare su valori - la copia di valori e confrontando i valori, rispettivamente.


6
È piuttosto semplice davvero ... Fai solo Python e non hai compiti sovraccarichi.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳,

225
Questa risposta non risponde affatto alla domanda. Stai semplicemente parlando dell'uso del segno uguale da parte di java. Se b + C restituisse un nuovo complesso, allora a = b + c sarebbe perfettamente valido e sì molto più semplice da leggere. Anche se volessi modificare un posto, a.set (b + c) è molto più semplice da leggere, specialmente quando l'aritmetica è più che banale: a.set ((a b + b c) / 5) o a = a.multiply (b) .add (b.multiply (c)). divide (5). La tua scelta ..
BT

24
O immagino .. non è una tua scelta, a seconda dei casi
BT

9
In C ++, i modelli di espressione risolvono il problema della copia aggiuntiva. Praticamente tutte le principali biblioteche aritmetiche usano questa tecnica proprio per questo motivo. Inoltre, questo non affronta la domanda, poiché a = b + c è solo zucchero sintattico per a.foo (b.bar (c)), che è in realtà l'osservazione iniziale nella domanda.
Kaz Dragon,

18
Questa non è la risposta alla domanda posta. Queste sono le speculazioni di qualcuno su alcune differenze tra Java e C ++.
SChepurin,

804

Ci sono molti post che si lamentano del sovraccarico dell'operatore.

Sentivo di dover chiarire i concetti di "sovraccarico dell'operatore", offrendo un punto di vista alternativo su questo concetto.

Codice offuscante?

Questo argomento è un errore.

Offuscare è possibile in tutte le lingue ...

È facile offuscare il codice in C o Java attraverso funzioni / metodi come lo è in C ++ tramite sovraccarichi dell'operatore:

// 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 ;
}

... Anche nelle interfacce standard di Java

Per un altro esempio, vediamo l' Cloneableinterfaccia in Java:

Dovresti clonare l'oggetto che implementa questa interfaccia. Ma potresti mentire. E crea un oggetto diverso. In effetti, questa interfaccia è così debole che potresti restituire un altro tipo di oggetto, solo per divertimento:

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

Dato che l' Cloneableinterfaccia può essere abusata / offuscata, dovrebbe essere vietata per gli stessi motivi che si suppone dovrebbe essere il sovraccarico dell'operatore C ++?

Potremmo sovraccaricare il toString()metodo di una MyComplexNumberclasse per far sì che restituisca l'ora del giorno rigorosa. Anche il toString()sovraccarico dovrebbe essere vietato? Potremmo sabotare MyComplexNumber.equalsper avere un valore casuale, modificare gli operandi ... ecc. Ecc. Ecc.

In Java, come in C ++ o in qualsiasi linguaggio, il programmatore deve rispettare un minimo di semantica durante la scrittura del codice. Ciò significa implementare una addfunzione che aggiunge e un Cloneablemetodo di implementazione che clona e un ++operatore che incrementa.

Cosa c'è di offuscante comunque?

Ora che sappiamo che il codice può essere sabotato anche attraverso i metodi Java incontaminati, possiamo chiederci quale sia il reale utilizzo del sovraccarico dell'operatore in C ++?

Notazione chiara e naturale: metodi contro sovraccarico dell'operatore?

Confronteremo di seguito, per casi diversi, lo "stesso" codice in Java e C ++, per avere un'idea di quale tipo di stile di codifica sia più chiaro.

Confronti naturali:

// 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 ;

Si noti che A e B potrebbero essere di qualsiasi tipo in C ++, purché siano previsti sovraccarichi dell'operatore. In Java, quando A e B non sono primitivi, il codice può diventare molto confuso, anche per oggetti di tipo primitivo (BigInteger, ecc.) ...

Accessori e sottoscrizioni di array / container naturali:

// 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

In Java, vediamo che per ogni contenitore fare la stessa cosa (accedere al suo contenuto attraverso un indice o identificatore), abbiamo un modo diverso di farlo, il che è confuso.

In C ++, ogni container utilizza lo stesso modo per accedere al suo contenuto, grazie al sovraccarico dell'operatore.

Manipolazione di tipi avanzati naturali

Gli esempi seguenti usano un Matrixoggetto, trovato usando i primi collegamenti trovati su Google per " oggetto Matrix Java " e " oggetto Matrix C ++ ":

// 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

E questo non si limita alle matrici. Le classi BigIntegere BigDecimaldi Java soffrono della stessa confusione confusa, mentre i loro equivalenti in C ++ sono chiari come i tipi incorporati.

Iteratori naturali:

// 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

Funzionari naturali:

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

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

Concatenazione del testo:

// 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, anche in Java puoi usare MyString = "Hello " + 25 + " World" ;... Ma aspetta un secondo: questo è un sovraccarico dell'operatore, vero? Non è barare ???

:-D

Codice generico?

Gli stessi operandi di modifica del codice generico dovrebbero essere utilizzabili sia per incorporati / primitivi (che non hanno interfacce in Java), oggetti standard (che non potrebbero avere la giusta interfaccia) e oggetti definiti dall'utente.

Ad esempio, calcolando il valore medio di due valori di tipi arbitrari:

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

Discutere del sovraccarico dell'operatore

Ora che abbiamo visto equi confronti tra il codice C ++ utilizzando il sovraccarico dell'operatore e lo stesso codice in Java, ora possiamo discutere di "sovraccarico dell'operatore" come concetto.

Il sovraccarico dell'operatore esisteva da prima dei computer

Anche al di fuori di informatica, non v'è l'overloading degli operatori: ad esempio, in matematica, operatori come +, -, *, ecc sono sovraccarichi.

Infatti, il significato di +, -, *, ecc cambia a seconda dei tipi degli operandi (i numeri, vettori, funzioni onda quantistica, matrici, ecc).

La maggior parte di noi, nell'ambito dei nostri corsi di scienze, ha imparato molteplici significati per gli operatori, a seconda dei tipi di operandi. Li abbiamo trovati confusi, loro?

Il sovraccarico dell'operatore dipende dai suoi operandi

Questa è la parte più importante del sovraccarico dell'operatore: come in matematica o in fisica, l'operazione dipende dai tipi di operandi.

Quindi, conosci il tipo di operando e conoscerai l'effetto dell'operazione.

Perfino C e Java hanno un sovraccarico dell'operatore (codificato)

In C, il comportamento reale di un operatore cambierà in base ai suoi operandi. Ad esempio, l'aggiunta di due numeri interi è diversa dall'aggiunta di due doppi, o anche un numero intero e un doppio. Esiste anche l'intero dominio aritmetico del puntatore (senza casting, puoi aggiungere a un puntatore un numero intero, ma non puoi aggiungere due puntatori ...).

In Java non esiste l'aritmetica del puntatore, ma qualcuno ha ancora trovato che la concatenazione di stringhe senza l' +operatore sarebbe abbastanza ridicola da giustificare un'eccezione nel credo "sovraccarico dell'operatore è male".

È solo che tu, come programmatore C (per motivi storici) o Java (per motivi personali , vedi sotto), non puoi fornire il tuo.

In C ++, il sovraccarico dell'operatore non è facoltativo ...

In C ++, il sovraccarico dell'operatore per i tipi incorporati non è possibile (e questa è una buona cosa), ma i tipi definiti dall'utente possono avere sovraccarichi dell'operatore definiti dall'utente .

Come già detto in precedenza, in C ++, e al contrario di Java, i tipi di utenti non sono considerati cittadini di seconda classe della lingua rispetto ai tipi predefiniti. Quindi, se i tipi predefiniti hanno operatori, anche i tipi utente dovrebbero essere in grado di averli.

La verità è che, come il toString(), clone(), equals()metodi sono per Java ( cioè quasi standard simile ), C ++ overload degli operatori è tanto parte di C ++ che diventa naturale come gli operatori C originali, o la prima metodi Java citati.

In combinazione con la programmazione dei modelli, il sovraccarico dell'operatore diventa un modello di progettazione ben noto. In effetti, non puoi andare molto lontano in STL senza usare operatori sovraccaricati e sovraccaricare gli operatori per la tua classe.

... ma non dovrebbe essere abusato

Il sovraccarico dell'operatore dovrebbe cercare di rispettare la semantica dell'operatore. Non sottrarre in un +operatore (come in "non sottrarre in una addfunzione" o "restituire schifezze in un clonemetodo").

Il sovraccarico del cast può essere molto pericoloso perché può portare ad ambiguità. Quindi dovrebbero davvero essere riservati per casi ben definiti. Per quanto riguarda &&e ||, non li sovraccarichi mai a meno che tu non sappia davvero cosa stai facendo, poiché perderai la valutazione del corto circuito che gli operatori nativi &&e ||apprezzano.

Quindi ... Ok ... Allora perché non è possibile in Java?

Perché James Gosling ha detto così:

Ho lasciato il sovraccarico dell'operatore come una scelta abbastanza personale perché avevo visto troppe persone abusarne in C ++.

James Gosling. Fonte: http://www.gotw.ca/publications/c_family_interview.htm

Si prega di confrontare il testo di Gosling sopra con quello di Stroustrup di seguito:

Molte decisioni di progettazione in C ++ hanno le mie radici nella mia antipatia per costringere le persone a fare le cose in un modo particolare [...] Spesso, ero tentato di mettere fuorilegge una caratteristica che non mi piaceva, mi sono astenuto dal farlo perché non pensavo di avere il diritto di forzare le mie opinioni sugli altri .

Bjarne Stroustrup. Fonte: The Design and Evolution of C ++ (1.3 General Background)

Il sovraccarico dell'operatore trarrebbe vantaggio da Java?

Alcuni oggetti trarrebbero grandi benefici dal sovraccarico dell'operatore (tipi concreti o numerici, come BigDecimal, numeri complessi, matrici, contenitori, iteratori, comparatori, parser ecc.).

In C ++, puoi trarre vantaggio da questo vantaggio a causa dell'umiltà di Stroustrup. In Java, sei semplicemente fregato a causa della scelta personale di Gosling .

Potrebbe essere aggiunto a Java?

Le ragioni per non aggiungere il sovraccarico dell'operatore ora in Java potrebbero essere un mix di politica interna, allergia alla funzionalità, sfiducia degli sviluppatori (sai, quelli sabotatori che sembrano perseguitare i team Java ...), compatibilità con le precedenti JVM, tempo di scrivere una specifica corretta, ecc.

Quindi non trattenere il respiro in attesa di questa funzione ...

Ma lo fanno in C # !!!

Si...

Mentre questa è ben lungi dall'essere l'unica differenza tra le due lingue, questa non manca mai di divertirmi.

Apparentemente, la gente di C #, con il loro "ogni primitivo è un struct, e structderiva da un oggetto" , ha capito bene al primo tentativo.

E lo fanno in altre lingue !!!

Nonostante tutto il FUD contro il sovraccarico dell'operatore definito utilizzato, le seguenti lingue lo supportano: 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 ...

Così tante lingue, con così tante filosofie diverse (e talvolta opposte), eppure sono tutte d'accordo su questo punto.

Cibo per la mente...


50
Questa è una risposta eccellente Non sono d'accordo, ma è comunque un'ottima risposta. Penso che i problemi possibili con sovraccarichi difettosi superino il valore dei sovraccarichi positivi.
Douglas Leeder,

69
@Douglas Leeder: Grazie! Il sovraccarico dell'operatore è come OOP. La prima volta che impari a farlo, scrivi sovraccarichi ovunque come metti classi base ed ereditarietà ovunque (come, ironia dolce, l'API Java). Ma questo passa abbastanza velocemente e quindi apprezzi la possibilità per tutto il tempo di non abusarne. La mia esperienza di oltre 10 anni sul C ++ è che il numero di sovraccarichi negativi che ho visto sia nel mio codice che nel codice di altri programmatori è così basso che credo di poterli contare su una mano. E questo è molto inferiore al numero di bug complessivi con sovraccarico sprintf, strcat, memset e buffer.
Paercebal,

11
@Douglas Leeder: Credo, dopo averne discusso in un'altra domanda SO, che il divario tra "amanti" e "odiatori" del sovraccarico dell'operatore è probabilmente causato da una differenza nel loro approccio al codice: "Gli Haters" sono più "funzioni sono ciò che conta ", nel senso che si aspettano che una funzione faccia una cosa e solo una cosa. Pertanto gli operatori dovrebbero lavorare come previsto dalla lingua. Gli "Amanti" riguardano più "gli oggetti devono comportarsi", nel senso che accettano più facilmente che la funzione (e quindi gli operatori) possono cambiare il loro comportamento in base al tipo di parametri.
paercebal,

103
Risposta epica. Uno dei debunks più qualificati che abbia mai letto.
Sebastian Mach,

7
@MaartenBodewes: tutti gli esempi che ho scritto sopra e tutto ciò che ti disturba è il "come sviluppatore, sei fregato perché la scelta personale di Gosling" ? Per favore, scrivi la tua risposta, difendendo l' angolazione "voi sviluppatori siete stupidi, lasciate che la gente geniale decida per voi ciò di cui avete bisogno" . Questa discussione non serve a nulla.
Paercebal,

44

James Gosling ha paragonato la progettazione di Java a quanto segue:

"C'è questo principio di trasferirsi, quando ti sposti da un appartamento a un altro appartamento. Un esperimento interessante è quello di impacchettare il tuo appartamento e mettere tutto in scatole, quindi spostarti nell'appartamento successivo e non disimballare nulla fino a quando non ne hai bisogno. Quindi ' stai preparando il tuo primo pasto e stai estraendo qualcosa da una scatola. Poi dopo circa un mese l'hai usato per capire praticamente quali cose della tua vita hai effettivamente bisogno, e poi prendi il resto del cose - dimentica quanto ti piace o quanto è bello - e lo butti via. È incredibile come ciò semplifichi la tua vita e puoi usare quel principio in tutti i tipi di problemi di design: non fare le cose solo perché sei figo o solo perché sono interessanti. "

Puoi leggere il contesto della citazione qui

Fondamentalmente il sovraccarico dell'operatore è ottimo per una classe che modella un qualche tipo di punto, valuta o numero complesso. Ma dopo inizi a esaurire rapidamente gli esempi.

Un altro fattore è stato l'abuso della funzionalità in C ++ da parte degli sviluppatori che sovraccarica gli operatori come "&&", "||", gli operatori del cast e, naturalmente, "nuovi". La complessità risultante dalla combinazione di ciò con il passaggio per valore ed eccezioni è ben trattata nel libro Eccezionale C ++ .


6
Potresti fornire un esempio di codice della "complessità del sovraccarico dell'operatore combinato con il pass-by-value e le eccezioni"? Nonostante alcuni anni abbia giocato con la lingua, possedendo e avendo letto tutti i libri efficaci / eccezionali su C ++, non riesco a capire cosa intendi con questo.
paercebal,

60
Ciò che funziona per James Gosling non funzionerà per tutti. È incredibilmente miope per aver estrapolato il suo "interessante" esperimento di imballaggio nel senso che "butta via tutto nel mondo che non mi serve, quindi nessuno può usare quella roba". Chiaramente non sa di cosa ho bisogno o utilizzo.
BT

49
@BT: La maggior parte Enlightning è punto di vista di Gosling rispetto al punto di vista di Stroustrup su questo tema: 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 ++, ampiamente viziato contro l'altro, Java, ampiamente apprezzato" Questo è un hype di marketing. Ricorda che il C ++ è cresciuto da solo, mentre Java (e .NET) hanno tratto profitto dai bulldozer di marketing. Non sembra strano che per un "linguaggio molto apprezzato", Java sia limitato alle applicazioni server, mentre "ampiamente viziato" (probabilmente da sviluppatori e manager Java che vogliono abbassare il costo della produzione di codice) il C ++ passa da molto alto- server ad alte prestazioni per giochi ad alte prestazioni? [...]
paercebal,

16
@Hassan: ogni lingua ha i suoi hack, i generici di Java ne sono un ottimo esempio. A proposito di 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: programmatori errati scriveranno codice errato indipendentemente dalla lingua. Prova a emulare un "pass-by-reference" per avere un'idea dei parametri di funzione in Java. Ho visto il codice e ho riso così forte da farmi male. Questo è il tipo di cose che Gosling non ha usato, quindi, aveva bisogno di orribili hack da avere in Java, eppure esiste nativamente, a costo zero, sia in C # che in C ++.
paercebal,

22

Dai un'occhiata a Boost.Units: testo del link

Fornisce un'analisi dimensionale zero overhead tramite sovraccarico dell'operatore. Quanto può essere più chiaro?

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

produrrebbe effettivamente "Energia = 4 J" che è corretta.


1
"Come esattamente se complica la manutenzione e dove mai questo codice offuscato?"
Mooing Duck,

13

I progettisti Java hanno deciso che il sovraccarico dell'operatore rappresentava più un problema che un errore. Semplice come quella.

In un linguaggio in cui ogni variabile di oggetto è in realtà un riferimento, il sovraccarico dell'operatore comporta il rischio aggiuntivo di essere piuttosto illogico, almeno per un programmatore C ++. Confrontare la situazione con == operatore C # 's sovraccarichi e Object.Equalse Object.ReferenceEquals(o come si chiama).


8

Groovy ha un sovraccarico dell'operatore e funziona nella JVM. Se non ti dispiace il colpo di prestazione (che diminuisce ogni giorno). È automatico in base ai nomi dei metodi. ad esempio, "+" chiama il metodo "più (argomento)".


4
Vorrei che tutte le lingue pesanti per la sintassi con sovraccarico dell'operatore avrebbero usato quella tecnica. Non ho mai capito perché debbano inventare una versione speciale della denominazione e della ricerca dei metodi. Stroustrup non menziona alcuna alternativa in D & EC ++. Il team di C # ha adottato l'approccio giusto con la sintassi di Linq ( where ...diventa .Where(i => ... ). Se solo avessero fatto lo stesso con gli operatori aritmetici, molte cose sarebbero state più semplici e più potenti. Java ha il vantaggio di una lavagna pulita e potrebbe farlo bene (anche se per motivi religiosi, probabilmente non lo farà mai).
Daniel Earwicker,

@DanielEarwicker, ho spesso notato che quando ci sono complicati disaccordi che le persone identificheranno le motivazioni per entrambe le parti come "religiose" in natura.

@noah, potrei vivere con un sottoinsieme limitato di sovraccarico dell'operatore come questo, a condizione che esistesse un tag speciale per i nomi dei metodi che li mantenga visivamente distinti. Qualcosa come definire un metodo __plus () per l'implementazione di un OL "+", e stare lontano sovraccaricando cose come cast e persino subscript di array. Ciò con cui non sono disposto a convivere è il modo in cui C ++ e C # hanno ritenuto opportuno implementarlo.

2
Non una risposta Esistono molte lingue in esecuzione nella macchina virtuale. Il sovraccarico dell'operatore non dovrebbe essere una buona ragione in sé per cambiare lingua.
Maarten Bodewes,

6

Penso che questa potrebbe essere stata una scelta progettuale consapevole per costringere gli sviluppatori a creare funzioni i cui nomi comunicano chiaramente le loro intenzioni. In C ++ gli sviluppatori sovraccaricherebbero gli operatori con funzionalità che spesso non avrebbero alcuna relazione con la natura comunemente accettata dell'operatore dato, rendendo quasi impossibile determinare cosa fa un pezzo di codice senza guardare alla definizione dell'operatore.


14
In C++ developers would overload operators with functionality that would often have no relation to the commonly accepted nature of the given operator: Questa è un'asserzione gratuita. Sono uno sviluppatore professionista C ++ da 12 anni e raramente ho riscontrato questo problema. In effetti, il maggior numero di bug e errori di progettazione che ho visto in C ++ sono stati nel codice in stile C ( void *, cast, ecc.)
paercebal,

6
-1. Ogni variabile che assegni è un simbolo, proprio come i simboli dell'operatore aritmetico. Indipendentemente dal fatto che tu usi una frase per nominare quella variabile, una sola parola o una sola lettera, è la tua (o la tua squadra) decisione. Chi può dire ciò che è significativo e ciò che non lo è? La risposta sei tu, il programmatore. Nella matematica pura, la moltiplicazione tra matrici significa qualcosa di diverso dalla moltiplicazione tra due numeri nell'aritmetica di base. Tuttavia usiamo gli stessi simboli per entrambi i tipi di moltiplicazione.
Ingegnere

2
@paercebal: l'affermazione è purtroppo corretta. Non devi guardare oltre IOstreams per vederlo in azione. Per fortuna la maggior parte degli sviluppatori è più avveduta nell'inventare nuove semantiche per gli operatori esistenti.
Ben Voigt,

5
@BenVoigt: [...] E non sto nemmeno menzionando il fatto che la addfunzione potrebbe essere davvero utilizzata in modo improprio (come fare una moltiplicazione o acquisire un mutex) ... L'abuso menzionato dall'utente 14128 non è limitato agli operatori, ma c'è una sorta di paura patologica sul sovraccarico dell'operatore che credo provenga dai primi giorni di C vs. C ++, una paura che non è stata modificata direttamente in Java, ma per fortuna, non è andata in C # ... Alla fine, rispettando la semantica e scrivere funzioni / operatori chiari è il compito dello sviluppatore. Non la lingua.
paercebal,

3
@ jbo5112: Esempio: cout << f() || g(); le parentesi non lo rendono più chiaro, lo rendono corretto. E se gli operatori del bit shift non fossero stati abusati, non sarebbero stati necessari. Perché è cout << (5&3) << endl;meglio di cout.fmt(5&3)(endl);? L'uso dell'operatore di chiamata di funzione su una variabile membro di funzione sarebbe una progettazione infinitamente migliore per i flussi rispetto alla riproposizione di operatori bit a bit solo perché il glifo ha un bell'aspetto. Ma questo è tutt'altro che l'unica cosa che non va nei flussi.
Ben Voigt,

5

Bene, puoi davvero spararti al piede con un sovraccarico dell'operatore. È come con i puntatori che le persone commettono errori stupidi con loro e quindi è stato deciso di togliere le forbici.

Almeno penso che sia questo il motivo. Sono dalla tua parte comunque. :)



2
È un pessimo modo di pensare. Puoi spararti al piede, piuttosto tagliamo le mani verso il basso, quindi non ce la farai. E ovviamente supponiamo che tu sia un idiota che si sparerà da solo.
ntj,

5

Alcune persone dicono che il sovraccarico dell'operatore in Java porterebbe a offuscamento. Quelle persone hanno mai smesso di guardare un codice Java facendo alcuni calcoli di base come aumentare un valore finanziario di una percentuale usando BigDecimal? .... la verbosità di un tale esercizio diventa la sua dimostrazione di offuscamento. Ironia della sorte, l'aggiunta di un sovraccarico dell'operatore a Java ci consentirebbe di creare la nostra classe Currency che renderebbe elegante e semplice tale codice matematico (meno offuscato).


4

Dire che il sovraccarico dell'operatore porta a errori logici di tipo che l'operatore non corrisponde alla logica dell'operazione, è come non dire nulla. Lo stesso tipo di errore si verificherà se il nome della funzione non è appropriato per la logica operativa, quindi qual è la soluzione: eliminare la capacità di utilizzo della funzione !? Questa è una risposta comica: "Inappropriato per la logica di funzionamento", ogni nome di parametro, ogni classe, funzione o quant'altro può essere logicamente inappropriato. Penso che questa opzione dovrebbe essere disponibile in un rispettabile linguaggio di programmazione, e quelli che pensano che non sia sicuro - ehi, nessuno dice che devi usarlo. Consente di prendere il C #. Hanno abbassato i puntatori ma ehi - c'è un'istruzione 'codice non sicuro' - programma come preferisci a tuo rischio e pericolo.


4

Tecnicamente, c'è un sovraccarico dell'operatore in ogni linguaggio di programmazione che può gestire diversi tipi di numeri, ad esempio numeri interi e numeri reali. Spiegazione: Il termine sovraccarico significa che ci sono semplicemente diverse implementazioni per una funzione. Nella maggior parte dei linguaggi di programmazione sono previste diverse implementazioni per l'operatore +, una per i numeri interi, una per i reali, questo si chiama sovraccarico dell'operatore.

Ora, molte persone trovano strano che Java abbia un sovraccarico dell'operatore per l'operatore + per l'aggiunta di stringhe insieme, e da un punto di vista matematico questo sarebbe davvero strano, ma visto dal punto di vista dello sviluppatore di un linguaggio di programmazione, non c'è nulla di sbagliato nell'aggiungere un sovraccarico dell'operatore incorporato per l'operatore + per altre classi, ad esempio String. Tuttavia, la maggior parte delle persone concorda sul fatto che una volta aggiunto il sovraccarico incorporato per + per String, in genere è una buona idea fornire questa funzionalità anche per lo sviluppatore.

Un completamente in disaccordo con l'errore che l'operatore sovraccarica offusca il codice, in quanto questo è lasciato allo sviluppatore per decidere. È ingenuo pensare, e ad essere onesti, sta invecchiando.

+1 per l'aggiunta del sovraccarico dell'operatore in Java 8.


L'uso di Java +per concatenare qualsiasi cosa string-ish è IMHO piuttosto orribile, così come il sovraccarico di /in C e FORTRAN per la divisione intera e frazionaria. In molte versioni di Pascal, l'uso di operatori aritmetici su qualsiasi tipo numerico produrrà risultati numericamente equivalenti al cast degli operandi Real, sebbene i risultati che potrebbero non essere numeri interi debbano essere passati Trunco Roundprima che possano essere assegnati a numeri interi.
supercat

2

Supponendo che Java come linguaggio di implementazione, allora a, b e c sarebbero tutti riferimenti al tipo Complex con valori iniziali di null. Supponendo anche che Complex sia immutabile come il citato BigInteger e simile BigDecimal immutabile , penso che intendi quanto segue, poiché stai assegnando il riferimento al Complex restituito dall'aggiunta di bec, e non confrontando questo riferimento con a.

Non è:

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

molto più semplice di:

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

2
Sono io? ;) Uguali possono significare sia assegnazione che confronto, ma = è sempre assegnazione e == è sempre confronto. I nomi possono introdurre da soli grandi fonti di errori.

1

A volte sarebbe bello avere un sovraccarico dell'operatore, classi di amici e eredità multipla.

Comunque penso ancora che sia stata una buona decisione. Se Java avesse avuto un sovraccarico dell'operatore, non potremmo mai essere sicuri dei significati dell'operatore senza consultare il codice sorgente. Al momento non è necessario. E penso che anche il tuo esempio di utilizzo dei metodi anziché del sovraccarico dell'operatore sia abbastanza leggibile. Se vuoi rendere le cose più chiare, puoi sempre aggiungere un commento sopra le affermazioni pelose.

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

12
Naturalmente, come menzionato altrove, non si può mai essere sicuri del significato della funzione add.
Eclipse,

È vero, trovo ancora confortante sapere che almeno i miei operatori sono codificati. Certo, avere le funzionalità e usarle in modo sensato ci farebbe solo del bene. Il problema è che è difficile sapere se qualcuno li ha usati in modo sensato. E che sei d'accordo sulla definizione di sensatamente. :-)

1
Il commento aggiunto per chiarire il codice è l'aspetto del codice in una lingua che supportava il sovraccarico dell'operatore. Inoltre, il fatto che il commento sia scritto in termini di operatori nasconde la tua opposizione al sovraccarico degli operatori.
Aluan Haddad,

0

Questo non è un buon motivo per non consentirlo, ma pratico:

Le persone non lo usano sempre in modo responsabile. Guarda questo esempio dalla libreria di 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 |>>

Ecco la spiegazione:

L'operatore / è stato usato come operatore di composizione tra due livelli. In tal modo, il livello inferiore può avere uno o più dei campi predefiniti sovraccaricati in base al livello superiore. (Puoi ancora dare il valore che desideri). Una stringa può essere utilizzata come livello non elaborato.


0

Alternative al supporto nativo del sovraccarico dell'operatore Java

Poiché Java non ha un sovraccarico da parte dell'operatore, ecco alcune alternative che puoi esaminare:

  1. Usa un'altra lingua. Sia Groovy che Scala hanno un sovraccarico dell'operatore e sono basati su Java.
  2. Utilizzare java-oo , un plug-in che consente il sovraccarico dell'operatore in Java. Si noti che NON è indipendente dalla piattaforma. Inoltre, ha molti problemi e non è compatibile con le ultime versioni di Java (ovvero Java 10). ( Origine StackOverflow originale )
  3. Utilizzare JNI , Java Native Interface o alternative. Ciò consente di scrivere metodi C o C ++ (forse altri?) Da utilizzare in Java. Naturalmente anche questo NON è indipendente dalla piattaforma.

Se qualcuno è a conoscenza di altri, si prega di commentare e lo aggiungerò a questo elenco.


0

Sebbene il linguaggio Java non supporti direttamente il sovraccarico dell'operatore, è possibile utilizzare il plug- in del compilatore Manifold in qualsiasi progetto Java per abilitarlo. Supporta Java 8 - 13 (l'attuale versione Java) ed è completamente supportato in IntelliJ IDEA.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.