it-swarm.it

Perché Java non offre l'overloading dell'operatore?

Venendo da C++ a Java, l'ovvia domanda senza risposta è perché Java non ha incluso l'overloading dell'operatore?

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

C'è una ragione nota per questo, argomenti validi per non che consente il sovraccarico dell'operatore? La ragione è arbitraria o persa nel tempo?

375
rengolin

Supponendo che si desideri sovrascrivere il valore precedente dell'oggetto denominato a, è necessario richiamare 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 addizione e copy 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. Quindi per un tipo definito dall'utente 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à non uguale. In Java, operator= esegue la copia di riferimento, quindi a e b si riferiscono ora allo stesso valore. Di conseguenza, il confronto produrrà "uguale", poiché l'oggetto si confronterà uguale a se stesso.

La differenza tra copie e riferimenti aumenta solo la confusione dell'overloading dell'operatore. Come menzionato da @Sebastian, sia Java che C # hanno a che fare separatamente con il valore e l'uguaglianza di riferimento - operator+ probabilmente si occuperà 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== stanno entrambi lavorando sui valori - copiando i valori e confrontando i valori rispettivamente.

17
Aaron

James Gosling ha paragonato la progettazione di Java al seguente:

"C'è questo principio del movimento, quando ti sposti da un appartamento ad un altro appartamento: un esperimento interessante è quello di mettere in ordine il tuo appartamento e mettere tutto in scatole, quindi spostarti nel prossimo appartamento e non disfare nulla finché non ne hai bisogno. stai facendo il tuo primo pasto e stai tirando fuori qualcosa da una scatola, poi dopo un mese o giù di lì l'hai usato per capire più o meno quali sono le cose nella tua vita di cui hai effettivamente bisogno, e poi prendi il resto del roba - dimentica quanto ti piace o quanto è bello - e lo butti via. È incredibile come questo semplifica la tua vita, e puoi usare quel principio in tutti i tipi di problemi di progettazione: non fare le cose solo perché Sei cool o solo perché sono interessanti. "

Puoi leggere il contesto della citazione qui

Fondamentalmente l'overloading dell'operatore è ottimo per una classe che modella un qualche tipo di punto, valuta o numero complesso. Ma dopo ciò inizi a corto di esempi velocemente.

Un altro fattore è stato l'abuso della funzionalità in C++ da parte degli sviluppatori che sovraccaricano operatori come "&&", "||", gli operatori di cast e, naturalmente, "nuovo". La complessità risultante dalla combinazione di questo con il pass per il valore e le eccezioni è ben trattata nel libro Exceptional C++ .

39
Garth Gilmour

Scopri Boost.Units: link text

Fornisce un'analisi dimensionale zero-overhead tramite il 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;

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

20
user15793

I progettisti Java hanno deciso che il sovraccarico dell'operatore era più difficile di quanto valesse. Semplice come quella.

In un linguaggio in cui ogni variabile oggetto è in realtà un riferimento, il sovraccarico dell'operatore ottiene il rischio aggiuntivo di essere abbastanza illogico - almeno per un programmatore C++. Confronta la situazione con l'overloading dell'operatore == su C # e Object.Equals e Object.ReferenceEquals (o come si chiama).

11
Sebastian Redl

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

8
noah

Penso che questa possa essere stata una scelta 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 che cosa fa un pezzo di codice senza guardare la definizione dell'operatore.

6
user14128

Bene, puoi davvero spararti ai piedi con il sovraccarico dell'operatore. È come se gli indicatori facessero errori stupidi con loro e così è stato deciso di togliere le forbici.

Almeno penso che sia la ragione . Sono dalla tua parte lo stesso. :)

5
Sarien

Tecnicamente, c'è un sovraccarico dell'operatore in ogni linguaggio di programmazione che può gestire diversi tipi di numeri, ad es. numeri interi e reali. Spiegazione: Il termine overloading significa che esistono semplicemente diverse implementazioni per una funzione. Nella maggior parte dei linguaggi di programmazione vengono fornite diverse implementazioni per l'operatore +, uno per i numeri interi, uno per i veri, questo è chiamato overloading dell'operatore.

Ora, molte persone trovano strano che Java abbia un sovraccarico dell'operatore per l'operatore + per l'aggiunta di stringhe, e dal punto di vista matematico sarebbe davvero strano, ma visto dal punto di vista dello sviluppatore di un linguaggio di programmazione, non c'è nulla di male nell'aggiungere un sovraccarico dell'operatore incorporato per l'operatore + per altre classi es Stringa. Tuttavia, la maggior parte delle persone concorda sul fatto che una volta aggiunto overloading integrato per + per String, è generalmente consigliabile fornire questa funzionalità allo sviluppatore.

Un totale disaccordo con la fallacia che il sovraccarico dell'operatore offusca il codice, come questo è lasciato allo sviluppatore per decidere. Questo è ingenuo da pensare, e per essere onesti, sta invecchiando.

+1 per aggiungere l'overloading dell'operatore in Java 8.

4
Olai

Dire che l'overloading 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 di funzionamento, quindi qual è la soluzione: eliminare la capacità dell'uso della funzione !? Questa è una risposta comica - "Non appropriato per la logica operativa", ogni nome di parametro, ogni classe, funzione o qualsiasi cosa può essere logicamente inappropriata . Penso che questa opzione dovrebbe essere disponibile in un linguaggio di programmazione rispettabile, e quelli che pensano che sia non sicuro - hey no bothy dice che devi usarlo. Prendiamo il C #. Hanno abbassato i puntatori ma hey - c'è la frase 'codice non sicuro' - programma come ti piace a tuo rischio.

4
Kvant

Alcuni dicono che l'overloading dell'operatore in Java porterebbe all'offuscamento. Queste persone hanno mai smesso di guardare qualche codice Java facendo qualche matematica di base come aumentare un valore finanziario di una percentuale usando BigDecimal? .... la verbosità di un tale esercizio diventa la sua dimostrazione di obsfuscation. Ironia della sorte, l'aggiunta dell'overloading dell'operatore a Java ci consentirebbe di creare la nostra classe di valuta che renderebbe tale codice matematico elegante e semplice (meno offuscato).

3
Volksman

Supponendo che Java sia il linguaggio di implementazione, a, b e c saranno tutti riferimenti a tipo Complesso con valori iniziali di null. Partendo dal presupposto che Complex sia immutabile come BigInteger e simile immutable { BigDecimal , penso che tu intenda quanto segue, dato che stai assegnando il riferimento al Complesso restituito dall'aggiunta Be c, e non confrontando questo riferimento con a.

Non è:

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

much più semplice di:

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

A volte sarebbe bello avere un sovraccarico dell'operatore, classi di amici e ereditarietà 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 degli operatori senza guardare attraverso il codice sorgente. Al momento non è necessario. E penso che il tuo esempio di usare metodi invece di overloading dell'operatore sia anche abbastanza leggibile. Se vuoi rendere le cose più chiare puoi sempre aggiungere un commento sopra le dichiarazioni pelose.

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

Alternative al supporto nativo dell'overloading dell'operatore Java

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

  1. Usa un'altra lingua. Entrambi Groovy e Scala hanno overloading dell'operatore e sono basati su Java.
  2. Usa Java-oo , un plugin che abilita l'overloading dell'operatore in Java. Si noti che NON è indipendente dalla piattaforma. Inoltre, ha molti problemi e non è compatibile con le ultime versioni di Java (ad esempio Java 10). ( Origine originale StackOverflow )
  3. Utilizzare JNI , Java Native Interface o alternative. Questo ti permette di scrivere metodi C o C++ (forse altri?) Per l'uso in Java. Ovviamente anche questo NON è indipendente dalla piattaforma.

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

0
gagarwa

Questa non è una buona ragione per respingerla, ma una pratica:

Le persone non lo usano sempre in modo responsabile. Guarda questo esempio dalla serie di librerie 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 utilizzato come operatore di composizione tra due strati. Quando lo fai, il livello inferiore può avere uno o più dei suoi i campi di default sono sovraccarichi in base al livello superiore. (Puoi ancora dare il valore che desideri). Una stringa può essere usata come un livello grezzo.

0
Sarien