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?
java
operator-overloading
rengolin
fuente
fuente
Respuestas:
Suponiendo que desea sobrescribir el valor anterior del objeto al que se hace referencia
a
, entonces se debería invocar una función miembro.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 llamadoComplex
, asignación significa copiar una referencia a un valor existente.Considere en su lugar:
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ía
yb
ahora 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, perooperator=
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=
yoperator==
están trabajando en los valores - la copia de los valores y la comparación de los valores, respectivamente.fuente
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:
... Incluso en las interfaces estándar de Java
Para otro ejemplo, veamos la
Cloneable
interfaz 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:
Como
Cloneable
se 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 unaMyComplexNumber
clase para que devuelva la hora en cadena del día. ¿DeberíatoString()
prohibirse también la sobrecarga? Podríamos sabotearMyComplexNumber.equals
para 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
add
función que agregue, y unCloneable
mé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:
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:
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
Matrix
objeto, que se encuentra utilizando los primeros enlaces encontrados en Google para " objeto Matrix Java " y " Objeto Matrix C ++ ":Y esto no se limita a las matrices. Las clases
BigInteger
yBigDecimal
de Java sufren de la misma verbosidad confusa, mientras que sus equivalentes en C ++ son tan claros como los tipos incorporados.Iteradores naturales:
Functores naturales:
Concatenación de texto:
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:
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 unaadd
función" o "devolver basura en unclone
mé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:
Compare el texto de Gosling arriba con el de Stroustrup a continuación:
¿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 unstruct
deriva 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...
fuente
James Gosling comparó el diseño de Java con lo siguiente:
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 ++ .
fuente
Many C++ design decisions have their roots in my dislike for forcing people to do things in some particular way [...] Often, I was tempted to outlaw a feature I personally disliked, I refrained from doing so because I did not think I had the right to force my views on others. (B. Stroustrup)
.I'd like them to go have a look at some C++ code out there that is hideously put together with weird hacks and "exceptional" features of the language
: 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 ++.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?
en realidad generaría "Energía = 4 J", lo cual es correcto.
fuente
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.Equals
yObject.ReferenceEquals
(o como se llame).fuente
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)'.
fuente
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á).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.
fuente
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.)add
funció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.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é escout << (5&3) << endl;
mejor quecout.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.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. :)
fuente
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).
fuente
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.
fuente
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.
fuente
+
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 operandosReal
, aunque los resultados que podrían no ser números enteros se deben alimentarTrunc
oRound
antes de que puedan asignarse a enteros.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.
fuente
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.
fuente
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:
Aquí está la explicación:
fuente
Alternativas al soporte nativo de sobrecarga del operador Java
Dado que Java no tiene sobrecarga del operador, aquí hay algunas alternativas que puede considerar:
Si alguien conoce a otros, comente y lo agregaré a esta lista.
fuente
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.
fuente