¿Por qué Java no ofrece sobrecarga del operador?


407

Viniendo de C ++ a Java, la pregunta obvia sin respuesta es ¿por qué Java no incluyó la sobrecarga del operador?

¿No es Complex a, b, c; a = b + c;mucho más simple que Complex a, b, c; a = b.add(c);?

¿Hay alguna razón conocida para esto, argumentos válidos para no permitir la sobrecarga del operador? ¿Es la razón arbitraria o perdida en el tiempo?



1
@zzzz, me cuesta leer ese artículo. ¿Se tradujo automáticamente o el inglés es el segundo idioma del escritor? Me parece que la discusión aquí es mucho más limpia.

25
Para el grupo de personas que cerraron esto como no constructivo, esta pregunta ha producido algunos de los diálogos más constructivos que he visto en SO. Tal vez sea un mejor candidato para programmers.stackexchange.com , pero hay momentos en que creo que SO está siendo demasiado despectivo con temas más amplios.

@NoNaMe es fácil, solo inserta mentalmente un y el - los artlcles perdidos es un obvio que la persona no es un hablante nativo de inglés o un programador (o como este tipo, ambos :) La razón por la que los programadores pueden dejar artículos es que puede haga que los comentarios sean más cortos y se ajusten más fácilmente en el espacio provisto ... a partir de ahí, simplemente se acostumbran. Mi problema es con el diseño, de alguna manera siempre estoy golpeando ese sitio en las búsquedas de Google. Afortunadamente, hay una gran extensión de Chrome llamada Clearly que reformatea las páginas difíciles de leer maravillosamente.
ycomp

1
No veo ninguna razón por qué y cómo OP aceptó la primera respuesta. La respuesta escrita por @ stackoverflow.com/users/14089/paercebal es excelente. Debería ser aceptado.
Destructor

Respuestas:


13

Suponiendo que desea sobrescribir el valor anterior del objeto al que se hace referencia a, entonces se debería invocar una función miembro.

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

En C ++, esta expresión le dice al compilador que cree tres (3) objetos en la pila, realice la suma y copie el valor resultante del objeto temporal en el objeto existente a.

Sin embargo, en Java, operator=no realiza una copia de valor para los tipos de referencia, y los usuarios solo pueden crear nuevos tipos de referencia, no tipos de valor. Entonces, para un tipo definido por el usuario llamado Complex, asignación significa copiar una referencia a un valor existente.

Considere en su lugar:

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

En C ++, esto copia el valor, por lo que la comparación no será igual. En Java, operator=de referencia se ejecuta la copia, así ay bahora están refiriendo al mismo valor. Como resultado, la comparación producirá 'igual', ya que el objeto se comparará igual a sí mismo.

La diferencia entre copias y referencias solo aumenta la confusión de la sobrecarga del operador. Como mencionó @Sebastian, Java y C # tienen que lidiar con el valor y la igualdad de referencia por separado, operator+probablemente trataría con valores y objetos, pero operator=ya está implementado para tratar con referencias.

En C ++, solo debe tratar con un tipo de comparación a la vez, por lo que puede ser menos confuso. Por ejemplo, en Complex, operator=y operator==están trabajando en los valores - la copia de los valores y la comparación de los valores, respectivamente.


66
Es bastante simple en realidad ... Simplemente me gusta Python y no tengo una tarea sobrecargada.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

225
Esta respuesta no responde a la pregunta en absoluto. Simplemente estás insistiendo en el uso de Java del signo igual. Si b + C devuelve un nuevo complejo, entonces a = b + c sería perfectamente válido, y sí, mucho más fácil de leer. Incluso si quisiera modificar a en su lugar, a.set (b + c) es mucho más fácil de leer, especialmente cuando la aritmética es más que trivial: a.set ((a b + b c) / 5) o a = a.multiply (b) .add (b.multiply (c)). divide (5). Tu elección ..
BT

24
O supongo ... no es tu elección, según sea el caso
BT

99
En C ++, las plantillas de expresión resuelven el problema de la copia adicional. Casi todas las bibliotecas aritméticas principales usan esta técnica por esta misma razón. Además, esto no responde a la pregunta, ya que a = b + c es solo azúcar sintáctico para a.foo (b.bar (c)), que es realmente la observación inicial de la pregunta.
Kaz Dragon

18
Esta no es la respuesta a la pregunta formulada. Estas son las especulaciones de alguien sobre ciertas diferencias entre Java y C ++.
SChepurin

805

Hay muchas publicaciones quejándose de la sobrecarga del operador.

Sentí que tenía que aclarar los conceptos de "sobrecarga del operador", ofreciendo un punto de vista alternativo sobre este concepto.

Código de ofuscación?

Este argumento es una falacia.

La ofuscación es posible en todos los idiomas ...

Es tan fácil ofuscar el código en C o Java a través de funciones / métodos como lo es en C ++ a través de sobrecargas del operador:

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

... Incluso en las interfaces estándar de Java

Para otro ejemplo, veamos la Cloneableinterfaz en Java:

Se supone que debe clonar el objeto que implementa esta interfaz. Pero podrías mentir. Y crea un objeto diferente. De hecho, esta interfaz es tan débil que podría devolver otro tipo de objeto por completo, solo por el gusto de hacerlo:

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

Como Cloneablese puede abusar / ofuscar la interfaz, ¿debería prohibirse por los mismos motivos que se supone que es la sobrecarga del operador de C ++?

Podríamos sobrecargar el toString()método de una MyComplexNumberclase para que devuelva la hora en cadena del día. ¿Debería toString()prohibirse también la sobrecarga? Podríamos sabotear MyComplexNumber.equalspara que devuelva un valor aleatorio, modifique los operandos ... etc. etc. etc.

En Java, como en C ++, o cualquier otro lenguaje, el programador debe respetar un mínimo de semántica al escribir código. Esto significa implementar una addfunción que agregue, y un Cloneablemétodo de implementación que clone, y un ++operador que incremente.

¿Qué es lo que ofusca de todos modos?

Ahora que sabemos que el código puede ser saboteado incluso a través de los prístinos métodos de Java, podemos preguntarnos sobre el uso real de la sobrecarga de operadores en C ++.

Notación clara y natural: ¿métodos frente a sobrecarga del operador?

Compararemos a continuación, para diferentes casos, el "mismo" código en Java y C ++, para tener una idea de qué tipo de estilo de codificación es más claro.

Comparaciones naturales:

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

Tenga en cuenta que A y B pueden ser de cualquier tipo en C ++, siempre que se proporcionen las sobrecargas del operador. En Java, cuando A y B no son primitivas, el código puede volverse muy confuso, incluso para objetos primitivos (BigInteger, etc.) ...

Arreglos naturales / contenedores y suscriptores:

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

En Java, vemos que para que cada contenedor haga lo mismo (acceder a su contenido a través de un índice o identificador), tenemos una forma diferente de hacerlo, lo cual es confuso.

En C ++, cada contenedor utiliza la misma forma de acceder a su contenido, gracias a la sobrecarga del operador.

Manipulación natural de tipos avanzados

Los ejemplos a continuación utilizan un Matrixobjeto, que se encuentra utilizando los primeros enlaces encontrados en Google para " objeto Matrix Java " y " Objeto 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

Y esto no se limita a las matrices. Las clases BigIntegery BigDecimalde Java sufren de la misma verbosidad confusa, mientras que sus equivalentes en C ++ son tan claros como los tipos incorporados.

Iteradores naturales:

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

Functores naturales:

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

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

Concatenación de texto:

// 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, en Java también puedes usar MyString = "Hello " + 25 + " World" ;... Pero, espera un segundo: esto es sobrecarga del operador, ¿no? ¿No es trampa?

:-RE

Código genérico?

Los mismos operandos modificadores del código genérico deberían ser utilizables tanto para elementos integrados / primitivos (que no tienen interfaces en Java), objetos estándar (que no podrían tener la interfaz correcta) y objetos definidos por el usuario.

Por ejemplo, calcular el valor promedio de dos valores de tipos arbitrarios:

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

Discutir sobrecarga del operador

Ahora que hemos visto comparaciones justas entre el código C ++ usando la sobrecarga del operador y el mismo código en Java, ahora podemos discutir la "sobrecarga del operador" como un concepto.

La sobrecarga del operador existía desde antes de las computadoras

Incluso fuera de la informática, no hay sobrecarga de operadores: Por ejemplo, en matemáticas, operadores como +, -, *, etc., están sobrecargados.

De hecho, la significación de +, -, *, etc. cambios dependiendo de los tipos de los operandos (Numerics, vectores, funciones de onda cuántica, matrices, etc.).

La mayoría de nosotros, como parte de nuestros cursos de ciencias, aprendimos múltiples significados para los operadores, dependiendo de los tipos de operandos. ¿Los encontramos confusos, ellos?

La sobrecarga del operador depende de sus operandos

Esta es la parte más importante de la sobrecarga del operador: como en matemáticas o en física, la operación depende de los tipos de sus operandos.

Entonces, conozca el tipo de operando, y sabrá el efecto de la operación.

Incluso C y Java tienen una sobrecarga del operador (codificada)

En C, el comportamiento real de un operador cambiará de acuerdo con sus operandos. Por ejemplo, agregar dos enteros es diferente de agregar dos dobles, o incluso uno entero y uno doble. Incluso existe todo el dominio aritmético del puntero (sin conversión, puede agregar a un puntero un número entero, pero no puede agregar dos punteros ...).

En Java, no existe una aritmética de puntero, pero alguien todavía encontró la concatenación de cadenas sin el +operador sería lo suficientemente ridículo como para justificar una excepción en el credo "la sobrecarga del operador es malo".

Es solo que usted, como codificador C (por razones históricas) o Java (por razones personales , ver más abajo), no puede proporcionar el suyo.

En C ++, la sobrecarga del operador no es opcional ...

En C ++, la sobrecarga del operador para los tipos integrados no es posible (y esto es algo bueno), pero los tipos definidos por el usuario pueden tener sobrecargas del operador definidas por el usuario .

Como ya se dijo anteriormente, en C ++, y al contrario de Java, los tipos de usuario no se consideran ciudadanos de segunda clase del lenguaje, en comparación con los tipos incorporados. Por lo tanto, si los tipos incorporados tienen operadores, los tipos de usuarios también deberían poder tenerlos.

La verdad es que, como el toString(), clone(), equals()métodos son para Java ( es decir, cuasi-estándar similar ), C ++ sobrecarga de operadores es tanto parte de C ++ que se convierte en tan natural como los operadores C originales, o la antes métodos Java mencionados.

En combinación con la programación de plantillas, la sobrecarga del operador se convierte en un patrón de diseño bien conocido. De hecho, no puede llegar muy lejos en STL sin usar operadores sobrecargados y sobrecargar operadores para su propia clase.

... pero no debe ser abusado

La sobrecarga del operador debe esforzarse por respetar la semántica del operador. No reste en un +operador (como en "no restar en una addfunción" o "devolver basura en un clonemétodo").

La sobrecarga de yeso puede ser muy peligrosa porque puede generar ambigüedades. Por lo tanto, realmente deberían reservarse para casos bien definidos. En cuanto a &&y ||, no siempre sobrecargarlos menos que realmente sepa lo que está haciendo, ya que si no se pierden la evaluación del cortocircuito que los operadores nativos &&y ||disfrutar.

Entonces ... Ok ... ¿Entonces por qué no es posible en Java?

Porque James Gosling lo dijo:

Dejé la sobrecarga del operador como una opción bastante personal porque había visto a muchas personas abusar de ella en C ++.

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

Compare el texto de Gosling arriba con el de Stroustrup a continuación:

Muchos de C ++ decisiones de diseño tienen sus raíces en mi aversión por obligar a la gente a hacer las cosas de cierta manera particular, [...] A menudo, tuve la tentación de prohibir una característica que personalmente no le gustaba, me abstenido de hacerlo porque yo no creía que tuviera El derecho a forzar mis puntos de vista sobre los demás .

Bjarne Stroustrup. Fuente: El diseño y la evolución de C ++ (1.3 Antecedentes generales)

¿La sobrecarga del operador beneficiaría a Java?

Algunos objetos se beneficiarían enormemente de la sobrecarga del operador (tipos concretos o numéricos, como BigDecimal, números complejos, matrices, contenedores, iteradores, comparadores, analizadores, etc.).

En C ++, puede beneficiarse de este beneficio debido a la humildad de Stroustrup. En Java, simplemente estás jodido debido a la elección personal de Gosling .

¿Se podría agregar a Java?

Las razones para no agregar la sobrecarga de operadores ahora en Java podrían ser una combinación de políticas internas, alergia a la característica, desconfianza de los desarrolladores (ya sabes, los saboteadores que parecen perseguir a los equipos de Java ...), compatibilidad con las JVM anteriores, hora de escribir una especificación correcta, etc.

Así que no contengas la respiración esperando esta característica ...

¡Pero lo hacen en C #!

Si...

Si bien esto está lejos de ser la única diferencia entre los dos idiomas, este nunca deja de divertirme.

Aparentemente, la gente de C #, con su "todo primitivo es un struct, y un structderiva del Objeto" , acertó al primer intento.

¡Y lo hacen en otros idiomas !

A pesar de todo el FUD contra la sobrecarga del operador definido usado, los siguientes idiomas lo admiten: 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 ...

Tantos lenguajes, con tantas filosofías diferentes (y a veces opuestas) y, sin embargo, todos están de acuerdo en ese punto.

Comida para el pensamiento...


50
Esta es una excelente respuesta. No estoy de acuerdo con eso, pero sigue siendo una excelente respuesta. Creo que los problemas que son posibles con sobrecargas malas exceden el valor de las sobrecargas buenas.
Douglas Leeder

69
@ Douglas Leeder: ¡Gracias! La sobrecarga del operador es como OOP. La primera vez que aprende a hacerlo, escribe sobrecargas en todas partes, ya que colocaría las clases base y la herencia en todas partes (como, dulce ironía, la API de Java). Pero esto pasa bastante rápido y luego aprecia la posibilidad todo el tiempo sin abusar de ella. Mi propia experiencia de más de 10 años sobre C ++ es que la cantidad de sobrecargas malas que vi tanto en mi código como en el código de otros codificadores es tan baja que creo que podría contarlas por un lado. Y esto es mucho menor que el número total de errores con sprintf, strcat, memset y desbordamientos de búfer.
paercebal

11
@ Douglas Leeder: Creo que, después de discutir sobre eso en otra pregunta de SO, que la brecha entre los "amantes" y los "enemigos" de la sobrecarga del operador probablemente sea causada por una diferencia en su enfoque del código: "Los enemigos" son más "funciones" son lo que importa ", lo que significa que esperan que una función haga una cosa y solo una cosa. Por lo tanto, los operadores deben trabajar según lo diseñado por el lenguaje. Los "amantes" son más acerca de "los objetos deben comportarse", lo que significa que aceptan más fácilmente que la función (y, por lo tanto, los operadores) pueden cambiar su comportamiento de acuerdo con el tipo de sus parámetros.
paercebal

103
Respuesta épica Uno de los debunks más calificados que he leído.
Sebastian Mach

77
@MaartenBodewes: ¿Todos los ejemplos que escribí anteriormente, y todo lo que te molesta es "como desarrollador, estás jodido porque la elección personal de Gosling" ? Por favor, escriba su propia respuesta, defendiendo el ángulo "ustedes, los desarrolladores, son estúpidos, dejen que la gente genial decida por ustedes lo que necesitan" . Esta discusión no sirve para nada.
paercebal

44

James Gosling comparó el diseño de Java con lo siguiente:

"Existe este principio sobre la mudanza, cuando te mudas de un departamento a otro. Un experimento interesante es empacar tu departamento y poner todo en cajas, luego mudarte al siguiente departamento y no desempacar nada hasta que lo necesites. Así que ' estás haciendo tu primera comida, y estás sacando algo de una caja. Luego, después de un mes más o menos, lo has usado para averiguar qué cosas en tu vida realmente necesitas, y luego tomas el resto del tiempo. cosas: olvídate de lo que te gusta o lo genial que es, y simplemente bótala. Es sorprendente cómo eso simplifica tu vida, y puedes usar ese principio en todo tipo de problemas de diseño: no hacer las cosas solo porque eres genial o simplemente porque son interesantes ".

Puedes leer el contexto de la cita aquí

Básicamente, la sobrecarga del operador es excelente para una clase que modela algún tipo de punto, moneda o número complejo. Pero después de eso, comienzas a quedarte sin ejemplos rápidamente.

Otro factor fue el abuso de la función en C ++ por parte de los desarrolladores que sobrecargaron operadores como '&&', '||', los operadores de reparto y, por supuesto, 'nuevo'. La complejidad resultante de combinar esto con pasar por valor y excepciones está bien cubierta en el libro Excepcional de C ++ .


66
¿Podría proporcionar un ejemplo de código de "la complejidad de la sobrecarga del operador combinada con el paso por valor y las excepciones"? A pesar de algunos años jugando con el lenguaje, y de poseer y haber leído todos los libros efectivos / excepcionales en C ++, no entiendo lo que quieres decir con eso.
paercebal

6060
Lo que funciona para James Gosling no va a funcionar para todos. Es increíblemente miope por extrapolar su "interesante" experimento de empaque que significa "Desechar todo lo que no necesito en el mundo, para que nadie pueda usar esas cosas". Claramente no sabe lo que necesito o uso.
BT

49
@BT: La mayoría Enlightning es el punto de vista de Gosling si se compara con el punto de vista BS en este 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 ++, ampliamente vilipendiado frente al otro, Java, muy querido" Esta es una exageración de marketing. Recuerde que C ++ creció solo, mientras que Java (y .NET) se beneficiaron de las excavadoras de marketing. ¿No parece extraño que para un "lenguaje que le gusta mucho", Java se limite a las aplicaciones de servidor, mientras que "ampliamente vilipendiado" (probablemente por desarrolladores y gerentes de Java que desean reducir el costo de producción de código) C ++ pasa de ser muy alto? servidores de rendimiento para juegos de alto rendimiento? [...]
paercebal

16
@Hassan: cada idioma tiene sus trucos, los genéricos de Java son un gran ejemplo de eso. Ahora, sobre 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: Los programadores malos escribirán código malo sin importar el idioma. Solo trate de emular una "referencia por paso" para los parámetros de función en Java para tener una idea. He visto el código y me reí tanto que me dolió. Este es el tipo de cosas que Gosling no usó, por lo tanto, necesitaba hacks horribles para tener en Java, sin embargo, existe de forma nativa, a costo cero, tanto en C # como en C ++.
paercebal

22

Echa un vistazo a Boost.Units: enlace de texto

Proporciona análisis dimensional sin sobrecarga a través de la sobrecarga del operador. ¿Cuánto más claro puede ser esto?

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

en realidad generaría "Energía = 4 J", lo cual es correcto.


1
"¿Cómo exactamente si complica el mantenimiento y dónde en la tierra este código ofusca?"
Mooing Duck

13

Los diseñadores de Java decidieron que la sobrecarga del operador era más problemática de lo que valía la pena. Simple como eso.

En un lenguaje donde cada variable de objeto es en realidad una referencia, la sobrecarga del operador tiene el riesgo adicional de ser bastante ilógico, al menos para un programador de C ++. Comparar la situación con el operador == C # 's sobrecarga y Object.Equalsy Object.ReferenceEquals(o como se llame).


8

Groovy tiene sobrecarga del operador y se ejecuta en la JVM. Si no te importa el éxito en el rendimiento (que se reduce cada día). Es automático según los nombres de los métodos. por ejemplo, '+' llama al método 'más (argumento)'.


44
Desearía que todos los lenguajes con mucha sintaxis con sobrecarga del operador hubieran utilizado esa técnica. Nunca he entendido por qué tienen que inventar una versión especial de nomenclatura y búsqueda de métodos. Stroustrup no menciona ninguna alternativa en D y EC ++. El equipo de C # tomó el enfoque correcto con la sintaxis de Linq (se where ...convierte .Where(i => ... ). Si hubieran hecho lo mismo con los operadores aritméticos, muchas cosas serían más simples y poderosas. Java tiene la ventaja de una pizarra limpia y podría hacerlo bien (aunque por razones religiosas, probablemente nunca lo hará).
Daniel Earwicker

@DanielEarwicker, a menudo he notado que cuando hay desacuerdos complicados, las personas etiquetan las motivaciones de cada lado como de naturaleza "religiosa".

@noah, podría vivir con un subconjunto limitado de sobrecarga de operadores como este, siempre que haya una etiqueta especial para los nombres de los métodos que los mantengan visualmente distintos. Algo así como definir un método __plus () para la implementación de un OL "+" y mantenerse alejado de sobrecargar cosas como conversiones e incluso subíndices de matriz. Con lo que no estoy dispuesto a vivir es con la forma en que C ++ y C # se ajustaron para implementarlo.

2
No es una respuesta Hay muchos idiomas ejecutándose en la VM. La sobrecarga del operador no debería ser una buena razón en sí misma para cambiar de idioma.
Maarten Bodewes

6

Creo que esta puede haber sido una elección de diseño consciente para obligar a los desarrolladores a crear funciones cuyos nombres comuniquen claramente sus intenciones. En C ++, los desarrolladores sobrecargarían a los operadores con una funcionalidad que a menudo no tendría relación con la naturaleza comúnmente aceptada del operador dado, haciendo casi imposible determinar qué hace un fragmento de código sin mirar la definición del operador.


14
In C++ developers would overload operators with functionality that would often have no relation to the commonly accepted nature of the given operator: Esta es una afirmación gratuita. Soy un desarrollador profesional de C ++ desde hace 12 años, y rara vez me encontré con este problema. De hecho, la mayoría de los errores y errores de diseño que vi en C ++ estaban en el código de estilo C ( void *, lanzamientos, etc.)
paercebal

66
-1. Cada variable que asigna es un símbolo, al igual que los símbolos del operador aritmético. Si usa una frase para nombrar esa variable, una sola palabra o una sola letra, es su decisión (o la de su equipo). ¿Quién puede decir qué es significativo y qué no? La respuesta eres tú, el programador. Dentro de las matemáticas puras, la multiplicación entre matrices significa algo diferente a la multiplicación entre dos números en aritmética básica. Sin embargo, usamos los mismos símbolos para ambos tipos de multiplicación.
Ingeniero

2
@paercebal: La afirmación es desafortunadamente correcta. No tiene que mirar más allá de IOstreams para verlo en acción. Afortunadamente, la mayoría de los desarrolladores son más circunspectos sobre la invención de nuevas semánticas para los operadores existentes.
Ben Voigt

55
@BenVoigt: [...] Y ni siquiera estoy mencionando el hecho de que la addfunción podría ser realmente mal utilizada (como hacer una multiplicación o adquirir un mutex) ... El abuso mencionado por user14128 no se limita a los operadores, sino que Existe un tipo de miedo patológico sobre la sobrecarga del operador que creo que proviene de los primeros días de C vs. C ++, un miedo que no se modificó directamente en Java, pero afortunadamente, no entró en C # ... Al final, respetando la semántica y escribir funciones / operadores claros es el trabajo del desarrollador. No es el idioma.
paercebal

3
@ jbo5112: Ejemplo: cout << f() || g(); los paréntesis no lo aclaran, lo hacen correcto. Y si los operadores de desplazamiento de bits no estuvieran siendo abusados, no serían necesarios. ¿Por qué es cout << (5&3) << endl;mejor que cout.fmt(5&3)(endl);? Usar el operador de llamada de función en una variable miembro de functor sería un diseño infinitamente mejor para las transmisiones que reutilizar operadores bit a bit solo porque el glifo se ve bien. Pero esto está lejos de ser lo único malo con las transmisiones.
Ben Voigt

5

Bueno, realmente puedes dispararte en el pie con la sobrecarga del operador. Es como con los punteros que las personas cometen errores estúpidos con ellos, por lo que se decidió quitar las tijeras.

Al menos creo que esa es la razón. Estoy de tu lado de todos modos. :)


Como, por ejemplo, este estúpido error ...
Amir Kirsh

2
Esa es una muy mala forma de pensar. Puedes dispararte en el pie, preferimos cortarte las manos para que no puedas. Y, por supuesto, suponemos que eres un idiota que se disparará.
ntj

5

Algunas personas dicen que la sobrecarga de operadores en Java conduciría a la ofuscación. ¿Se han detenido esas personas para mirar algún código Java haciendo algunas matemáticas básicas como aumentar un valor financiero en un porcentaje usando BigDecimal? .... la verbosidad de tal ejercicio se convierte en su propia demostración de obsfuscation. Irónicamente, agregar la sobrecarga del operador a Java nos permitiría crear nuestra propia clase de moneda que haría que dicho código matemático fuera elegante y simple (menos confuso).


4

Decir que la sobrecarga del operador conduce a errores lógicos de tipo que el operador no coincide con la lógica de la operación, es como no decir nada. Se producirá el mismo tipo de error si el nombre de la función no es apropiado para la lógica de la operación, entonces, ¿cuál es la solución: eliminar la capacidad de uso de la función? Esta es una respuesta cómica: "Inapropiado para la lógica de operación", cada nombre de parámetro, cada clase, función o lo que sea puede ser lógicamente inapropiado. Creo que esta opción debería estar disponible en un lenguaje de programación respetable, y para aquellos que piensan que no es seguro, oye, no, ambos dicen que tienes que usarla. Tomemos el C #. Bajaron los punteros pero oye, hay una declaración de 'código inseguro', programa como quieras bajo tu propio riesgo.


4

Técnicamente, hay una sobrecarga del operador en cada lenguaje de programación que puede manejar diferentes tipos de números, por ejemplo, números enteros y reales. Explicación: El término sobrecarga significa que simplemente hay varias implementaciones para una función. En la mayoría de los lenguajes de programación se proporcionan diferentes implementaciones para el operador +, uno para enteros, uno para reales, esto se llama sobrecarga del operador.

Ahora, a muchas personas les resulta extraño que Java tenga una sobrecarga del operador para el operador + para agregar cadenas juntas, y desde un punto de vista matemático esto sería realmente extraño, pero visto desde el punto de vista del desarrollador de un lenguaje de programación, no hay nada de malo en agregar una sobrecarga del operador incorporado para el operador + para otras clases, por ejemplo, String. Sin embargo, la mayoría de la gente está de acuerdo en que una vez que agregue sobrecarga incorporada para + para String, generalmente es una buena idea proporcionar esta funcionalidad también para el desarrollador.

Estoy completamente en desacuerdo con la falacia de que la sobrecarga del operador ofusca el código, ya que esto queda para que el desarrollador decida. Es ingenuo pensar y, para ser sincero, se está haciendo viejo.

+1 para agregar sobrecarga del operador en Java 8.


El uso de Java +para concatenar todo string-ish es bastante IMHO, como lo es la sobrecarga de /C y FORTRAN para la división total y fraccional. En muchas versiones de Pascal, el uso de operadores aritméticos en cualquier tipo numérico arrojará resultados numéricamente equivalentes a la conversión de los operandos Real, aunque los resultados que podrían no ser números enteros se deben alimentar Trunco Roundantes de que puedan asignarse a enteros.
supercat

2

Suponiendo que Java como el lenguaje de implementación, entonces a, byc serían referencias al tipo Complejo con valores iniciales de nulo. Suponiendo también que el Complejo es inmutable como el BigInteger mencionado y el BigDecimal inmutable similar , creo que te refieres a lo siguiente, ya que estás asignando la referencia al Complejo devuelto al agregar byc, y no al comparar esta referencia con a.

No es:

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

mucho más simple que:

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

2
Soy yo? ;) Igual puede significar asignación o comparación, pero = siempre es asignación y == siempre es comparación. Los nombres pueden introducir grandes fuentes de errores en sí mismos.

1

A veces sería bueno tener una sobrecarga de operadores, clases de amigos y herencia múltiple.

Sin embargo, todavía creo que fue una buena decisión. Si Java hubiera tenido una sobrecarga del operador, entonces nunca podríamos estar seguros de los significados del operador sin consultar el código fuente. En la actualidad eso no es necesario. Y creo que su ejemplo de uso de métodos en lugar de sobrecarga del operador también es bastante legible. Si desea aclarar las cosas, siempre puede agregar un comentario sobre las declaraciones peludas.

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

12
Por supuesto, como se mencionó en otra parte, tampoco puede estar seguro del significado de la función de agregar.
Eclipse el

Es cierto que todavía me resulta reconfortante saber que al menos mis operadores están codificados. Por supuesto, tener las características y usarlas con sensatez solo nos haría bien. El problema es que es difícil saber si alguien los ha usado con sensatez. Y que está de acuerdo con la definición de sensatez. :-)

1
El comentario agregado para aclarar el código es cómo se vería el código en un lenguaje que admitiera la sobrecarga del operador. Además, el hecho de que el comentario esté escrito en términos de operadores desmiente su oposición a la sobrecarga de operadores.
Aluan Haddad

0

Esta no es una buena razón para no permitirlo, sino una práctica:

La gente no siempre lo usa de manera responsable. Mira este ejemplo de la biblioteca de Python scapy:

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

Aquí está la explicación:

El operador / se ha utilizado como operador de composición entre dos capas. Al hacerlo, la capa inferior puede tener uno o más de sus campos predeterminados sobrecargados de acuerdo con la capa superior. (Aún puede dar el valor que desea). Una cadena se puede usar como una capa sin procesar.


0

Alternativas al soporte nativo de sobrecarga del operador Java

Dado que Java no tiene sobrecarga del operador, aquí hay algunas alternativas que puede considerar:

  1. Usa otro idioma. Tanto Groovy como Scala tienen sobrecarga del operador y se basan en Java.
  2. Use java-oo , un complemento que permite la sobrecarga del operador en Java. Tenga en cuenta que NO es independiente de la plataforma. Además, tiene muchos problemas y no es compatible con las últimas versiones de Java (es decir, Java 10). ( Fuente original de StackOverflow )
  3. Utilice JNI , Java Native Interface o alternativas. Esto le permite escribir métodos C o C ++ (¿quizás otros?) Para usar en Java. Por supuesto, esto tampoco es independiente de la plataforma.

Si alguien conoce a otros, comente y lo agregaré a esta lista.


0

Si bien el lenguaje Java no admite directamente la sobrecarga del operador, puede utilizar el complemento compilador Manifold en cualquier proyecto Java para habilitarlo. Es compatible con Java 8-13 (la versión actual de Java) y es totalmente compatible con IntelliJ IDEA.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.