unarios sobrecarga sencillos programacion operadores existe ejemplos ejemplo concepto binarios java c++ operator-overloading

sencillos - sobrecarga de operadores en java ejemplos



¿Por qué Java no ofrece sobrecarga de operadores? (15)

Al venir de C ++ a Java, la pregunta obvia sin respuesta es ¿por qué Java no incluyó la sobrecarga de operadores?

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

¿Existe una razón conocida para esto, argumentos válidos para no permitir la sobrecarga del operador? ¿Es la razón arbitraria, o se pierde en el tiempo?


Algunas personas dicen que la sobrecarga de operadores en Java llevaría a la ofuscación.¿Alguna vez se detuvo esa gente para ver algunos códigos Java que hacen algunos cálculos básicos como aumentar un valor financiero en un porcentaje usando BigDecimal? .... La verbosidad de tal ejercicio se convierte en su propia demostración de la ofuscación. Irónicamente, agregar una sobrecarga de operadores a Java nos permitiría crear nuestra propia clase de moneda que haría que este código matemático fuera elegante y simple (menos confuso).


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

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


Creo que esto 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, lo que hace que sea casi imposible determinar qué hace una pieza de código sin mirar la definición del operador.


Decir que la sobrecarga del operador conduce a errores lógicos de tipo que el operador no coincide con la lógica de operación, es como no decir nada. El mismo tipo de error ocurrirá si el nombre de la función no es apropiado para la lógica de operación. ¿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 lógicamente inapropiado. Creo que esta opción debería estar disponible en un lenguaje de programación respetable, y aquellos que piensan que es inseguro. Oye, ambos dicen que tienes que usarlo. Vamos a tomar el C #. Inclinaron los punteros pero, por su cuenta y riesgo, hay una declaración de "código no seguro" que usted desea.


Echa un vistazo a Boost.Units: texto del enlace

Proporciona un análisis dimensional de cero sobrecarga a través de la sobrecarga del operador. ¿Cuánto más claro puede conseguir 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 daría como resultado "Energía = 4 J", que es correcto.


Hay una gran cantidad de 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 ofuscante?

Este argumento es una falacia.

Ofuscación es posible en todos los idiomas ...

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

// 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 interfaz Cloneable en Java:

Se supone que debes clonar el objeto implementando 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 diversión:

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

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

Podríamos sobrecargar el método toString() de una clase MyComplexNumber para que devuelva la hora del día en cadena. ¿Debería prohibirse también la sobrecarga de toString() ? Podríamos sabotear MyComplexNumber.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 función de add que agregue, y Cloneable método de implementación Cloneable que Cloneable , y un operador ++ que aumente.

¿Qué es ofuscante de todos modos?

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

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

A continuación, compararemos, 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 y cuando se proporcionen las sobrecargas del operador. En Java, cuando A y B no son primitivas, el código puede llegar a ser muy confuso, incluso para objetos de tipo primitivo (BigInteger, etc.) ...

Matrices naturales / contenedores de acceso y subíndices:

// 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 cada contenedor hacer 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 siguientes ejemplos utilizan un objeto Matrix , que se encuentra con los primeros enlaces encontrados en Google para " Java Matrix object " y " c ++ Matrix object ":

// 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 BigInteger y BigDecimal de Java sufren de la misma verbosidad confusa, mientras que sus equivalentes en C ++ son tan claros como los tipos incorporados.

Los 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

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

:-RE

Código genérico?

Los mismos operandos modificadores de 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) como objetos definidos por el usuario.

Por ejemplo, calculando 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.

Discutiendo la sobrecarga del operador

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

Sobrecarga del operador existía desde antes de las computadoras

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

De hecho, la significación de + , - , * , etc. cambia dependiendo de los tipos de operandos (numéricos, 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, según 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 de operadores: como en matemáticas o en física, la operación depende de los tipos de sus operandos.

Por lo tanto, conozca el tipo de operando y sabrá el efecto de la operación.

Incluso C y Java tienen sobrecarga de operadores (codificados)

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

En Java, no hay aritmética de punteros, pero alguien todavía encontró que la concatenación de cadenas sin el operador + sería lo suficientemente ridícula como para justificar una excepción en el credo de "la sobrecarga de operadores es mala".

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

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

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

Como ya se dijo anteriormente, en C ++, y al contrario de Java, los tipos de usuarios 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 usuario también deberían poder tenerlos.

La verdad es que, al igual que los métodos toString() , clone() , equals() son para Java ( es decir, casi estándar ), la sobrecarga de operadores en C ++ es una parte tan importante de C ++ que se vuelve tan natural como los operadores C originales. , o los métodos Java antes mencionados.

Combinada 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 ir 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 función de add ", o "devolver basura en un método de clone ").

La sobrecarga de fundición puede ser muy peligrosa porque puede llevar a ambigüedades. Así que realmente deberían estar reservados para casos bien definidos. En cuanto a && y || , no los sobrecargue a menos que realmente sepa lo que está haciendo, ya que perderá la evaluación de cortocircuito que los operadores nativos && y || disfrutar.

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

Porque James Gosling lo dijo así:

Dejé de lado la sobrecarga de operadores como una elección bastante personal porque había visto a demasiadas personas abusar de ella en C ++.

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

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

Muchas decisiones de diseño de C ++ tienen su origen en mi disgusto por forzar a las personas a hacer las cosas de alguna manera en particular. A menudo, me sentí tentado a prohibir una característica que personalmente no me gustaba, me abstuve de hacerlo porque no creía que tuviera El derecho a forzar mi opinión 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.

¿Podría ser agregado 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 función, desconfianza de los desarrolladores (ya saben, los saboteadores que parecen acosar a los equipos de Java ...), compatibilidad con las JVM anteriores, tiempo para escribir una especificación correcta, etc.

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

Pero lo hacen en C # !!!

Sí...

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 una struct , y una struct deriva de Objeto" , lo hizo bien al primer intento.

¡Y lo hacen en otros idiomas !

A pesar de toda la FUD contra la sobrecarga del operador definido usado, los siguientes idiomas lo soportan: 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 (ya veces opuestas), y sin embargo todos están de acuerdo en ese punto.

Comida para el pensamiento...


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

"Hay un principio sobre la mudanza, cuando te mudas de un apartamento a otro. Un experimento interesante es empacar tu apartamento y poner todo en cajas, luego pasar al siguiente apartamento y no desempacar nada hasta que lo necesites. Así que '' está haciendo su primera comida y está sacando algo de una caja. Luego, después de un mes o más, lo ha usado para averiguar qué cosas en su vida realmente necesita, y luego toma el resto de la Cosas: olvida lo mucho que le gusta o lo genial que es, y simplemente lo tira. Es increíble cómo eso simplifica su vida, y puede usar ese principio en todo tipo de problemas de diseño: no haga las cosas solo porque son geniales o simplemente porque son interesantes ".

Puedes leer el http://www.gotw.ca/publications/c_family_interview.htm

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, te quedas 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 el paso por valor y las excepciones está bien cubierta en el libro Exceptional C ++ .


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

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


Suponiendo que desea sobrescribir el valor anterior del objeto al que hace referencia a , entonces debería invocarse 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 una adición 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. Por lo tanto, 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 resultará no igual. En Java, operator= realiza una copia de referencia, por lo que a y b ahora se refieren 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: el operator+ probablemente tratará con los valores y los objetos, pero el operator= ya está implementado para tratar las referencias.

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


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

Ahora, a muchas personas les resulta extraño que Java tenga una sobrecarga de operadores para el operador + para agregar cadenas, 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 la sobrecarga incorporada del operador. para el operador + para otras clases, por ejemplo, String. Sin embargo, la mayoría de las personas están de acuerdo en que una vez que agregue la sobrecarga incorporada para + para String, entonces generalmente es una buena idea proporcionar esta funcionalidad para el desarrollador también.

Está totalmente en desacuerdo con la falacia de que la sobrecarga del operador ofusca el código, ya que esto queda para que el desarrollador lo decida. Esto es ingenuo pensar, y para ser honesto, está envejeciendo.

+1 para agregar la sobrecarga de operadores en Java 8.


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


Alternativas al soporte nativo de la sobrecarga del operador Java

Como Java no tiene una sobrecarga de operadores, aquí hay algunas alternativas que puede considerar:

  1. Usa otro idioma. Tanto Groovy como Scala tienen sobrecarga de operadores, y están basados ​​en Java.
  2. Use java-oo , un complemento que permite la sobrecarga de operadores en Java. Tenga en cuenta que NO es plataforma independiente. Además, tiene muchos problemas y no es compatible con las últimas versiones de Java (es decir, Java 10). ( Fuente original de )
  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 plataforma independiente.

Si alguien está al tanto de otros, por favor comente y lo agregaré a esta lista.


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

Sin embargo sigo pensando que fue una buena decisión. Si Java hubiera tenido una sobrecarga de operadores, nunca podríamos estar seguros de los significados de los operadores sin mirar a través del código fuente. En la actualidad eso no es necesario. Y creo que su ejemplo de uso de métodos en lugar de sobrecarga de operadores también es bastante legible. Si quieres aclarar las cosas, siempre puedes agregar un comentario sobre las frases peludas.

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


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

La gente no siempre lo usa responsablemente. 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. (Todavía puedes dar el valor que quieras). Una cadena se puede utilizar como una capa en bruto.


Suponiendo que Java sea el lenguaje de implementación, entonces a, b y c serían referencias al tipo Complejo con valores iniciales de nulo. También asumiendo que Complex es inmutable como el BigInteger mencionado y BigDecimal similar inmutable , creo que te refieres a lo siguiente, ya que estás asignando la referencia al Complejo devuelto de la adición de b y c, y no comparando 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);