SlideShare una empresa de Scribd logo
1 de 9
Descargar para leer sin conexión
¿Por qué java no soporta la sobre carga de
operadores?
Java no soporta sobrecarga de operadores, aunque el sistema internamente la utiliza, pero está
ocultada al programador, por ejemplo si te fijas al hacer un
Int ejemplo=2+1;
En java eso es válido y le asigna un 3 a la variable ejemplo la funcionalidad fue sumar, sin embargo
en este:
String ejemplo="hola"+"mundo";
El operador + se utiliza con otra finalidad : Concatenar , lo ves? Java si usa la sobrecarga de
operadores, pero esta oculta para el programador, ósea no te permite usarla.

¿Por qué?
Básicamente para no llevar a confusión , imagina que defines una clase llamada "sobreEscribe" en
la cual sobrecargaras el operador suma y le asignaras tu propia forma de trabajar por ejemplo sumar
fracciones y más tarde defines la clase "fracciones" heredando la clase "sobreEscribe" el resultado
sería que en la clase "fracciones" el operador suma cambiara su comportamiento. Si llegase ahora
un programador externo y revisara la clase fracciones podría confundirse y creer que este operador
mantiene el comportamiento normal, para saber que tiene el comportamiento actual tendría que ir a
revisar "sobreEscribe", ahora imagina que esta herencia se realiza entre varias clases, sería muy
difícil saber si realmente se está cambiando el comportamiento del operador suma o no. Esta es una
de las razones más fuertes por las cuales Java no soporta la sobrecarga de operadores básicamente
para no llevar a confusión y al fin y al cabo no es necesaria, puedes utilizar tus propios métodos

¿Qué es la sobrecarga de operadores?
En C++ se pueden hacer operaciones con los tipos básicos del lenguaje: se pueden sumar enteros,
compararlos, etc. Lo siguiente es perfectamente válido en C++

int a=3;
int b=4;
int c;
c = a + b;
Si estos tipos no son los básicos del lenguaje, no se puede hacer sumas con ellos. Por ejemplo, si
ClaseC es una clase que tengo definida en C++, el siguiente código dará error.

ClaseC a;
ClaseC b;
ClaseC c;
c = a + b;
C++ permite que hagamos estas cosas si definimos en algún sitio cómo se suman esas clases.
Definiendo cómo se hacen las operaciones, podremos escribir las operaciones con nuestras clases
de la misma forma que si se trataran de tipos básicos del lenguaje.
De hecho podemos definir cualquier operador que nos dé C++, desde algunos muy normales como
suma, resta, multiplicación, mayor qué, etc, a otros un poco más raros como el operador () o el [], de
forma que ClaseC[i] o ClaseC(i,j) pueden devolver lo que nosostros queramos.

Sobrecarga del operador suma
Si tenemos una clase ComplejoC que representa un complejo, para poder sumar dos de estas
clases simplemente poniendo un +, como con cualquier tipo básico de C++, debemos sobrecargar
el operador +, darle una nueva funcionalidad.
La sobrecarga de un operador suma se hace de la siguiente manera

class ComplejoC
{
public:
// Permite sumar un ComplejoC con un double
ComplejoC operator + (double sumando);
// Permite sumar un ComplejoC con un array
ComplejoC operator + (const double sumando[]);
// Permite sumar dos ComplejoC entre sí.
ComplejoC operator + (const ComplejoC &sumando);
};
Aquí estamos redefiniendo tres operadores suma para que nos permita sumar a nuestra clase
ComplejoC cosas como un double, un array de double (que supondremos contiene dos double,
aunque no tenemos forma de comprobarlo) y otra clase ComplejoC .
En estas funciones hemos puesto const en los parámetros para no poder cambiarlos dentro del
código. En la tercera función pasamos ComplejoC por referencia (el &), para evitar que se hagan
copias innecesarias. En la primera función no es necesario nada de esto, puesto que es un simple
double. En la segunda, ponemos const para no poder modificar el array, pero no es necesaria la
referencia, puesto que los arrays son punteros.
A la hora de implementar debemos tener cuidado con los const que hemos puesto. Por ejemplo, en
el tercer operador +, recibimos un sumando que es const. El código que implementemos dentro no
puede modificar dicho sumando, ni puede llamar a ningún método de ese sumando que no esté
declarado como const. Si lo intentamos, el compilador dará error. Supongamos que ComplejoC
tiene un atributo double x (parte real) y un método dameX() para obtener dicho atributo, este método
debe estar declarado const para poder llamarlo desde nuestro operator +. Más o menos esto:

class ComplejoC
{
public:
// Atención al const del final. Sin el no podemos llamar a sumando.dameX().
double dameX() const;
};
De forma que en el operator + podemos llamar a sumando.dameX().
Una vez implementados estos métodos, podemos hacer operaciones como

ComplejoC a;
ComplejoC b;
// Aprovechando la primera sobrecarga
b = a + 1.0;
// Aprovechando la segunda sobrecarga
double c[] = {1.0, 2.0};
b = a + c;
// Aprovechando la tercera sobrecarga
b = a + b;
Cuando el compilador lee a + 1.0, lo interpreta como a.operator + (1.0), es decir, la llamada al
operador suma al que se le pasa como parámetro un double . De la misma forma sucede con los
otros dos operadores suma.
Sin embargo, esto nos da un pequeño problema. ¿Qué pasa si ponemos 1.0 + a?. Debería ser lo
mismo, pero al compilador le da un pequeño problema. Intenta llamar al método operator + de 1.0,
que no existe, puesto que 1.0 ni es una clase ni tiene métodos. Para solucionar este problema
tenemos la sobrecarga de operadores globales.
Sobrecarga de operadores suma globales
Un operador global es una función global que no pertenece a ninguna clase y que nos indica cómo
operar con uno o dos parámetros (depende del tipo de operador).

En nuestro ejemplo de las sumas, para poder poner los sumandos al revés, deberíamos definir las siguientes funciones globales:

ComplejoC operator + (double sumando1, const ComplejoC &sumando2);
ComplejoC operator + (double sumando1[], const ComplejoC &sumando2);
Estas funciones le indican al compilador cómo debe sumar los dos parámetros y qué devuelve. Con
ellas definidas e implementadas, ya podemos hacer

b = 1.0 + a;
// c era un array de double
b = c + a;
Esta sobrecarga es especialmente útil cuando tratamos con una clase ya hecha y que no podemos
modificar. Por ejemplo, cout es de la clase ostream y no podemos modificarla, sin embargo nos
sería de utilidad sobrecargar el operador << de ostream de forma que pueda escribir nuestros
números complejos. La siguiente llamada nos dará error mientras no redefinamos el operator <<
de ostream .

cout << a << endl;

// a es un ComplejoC

Con la sobrecarga de operadores globales podemos definir la función

ostream &operator << (ostream &salida, const ComplejoC &valor);
Con esta función definida, el complejo se escribirá en pantalla como indique dicha función. Esta
función deberá escribir la parte real e imaginaria del complejo en algún formato, utilizando algo
como

salida << valor.dameX() << " + " << valor.dameY() << "j";
El operador devuelve un ostream, que será un return cout. De esta forma se pueden encadenar
las llamadas a cout de la forma habitual.

cout << a << " + " << b << endl;
Primero se evalúa operator << (cout, a), que escribe a en pantalla y devuelve un cout, con lo que
la expresión anterior quedaría, después de evaluar esto

cout << " + " << b << endl;
Y así consecutivamente.
Hay que tener en cuenta que estos operadores globales no son de la clase, así que sólo pueden
acceder a métodos y atributos públicos de la misma.

El operador cast
Un operador interesante es el operador "cast". En C++, si tenemos dos tipos básicos distintos,
podemos pasar de uno a otro haciendo un cast (siempre que sean compatibles de alguna forma).
Por ejemplo

int a;
double b;
a = (int)b;
El cast consiste en poner delante, entre paréntesis, el tipo que queramos que sea. En algunos casos, como en el de este ejemplo,el

cast se hace automáticamente y no es necesario ponerlo. Puede que de un "warning" en el
compilador avisando de que perderemos los decimales.
En principio, con las clases no se puede hacer cast a otros tipos, pero es posible declarar operadores
que lo hagan. La sintaxis sería:

class ComplejoC
{
public:
// Permite hacer un cast de ComplejoC a double
operator double ();
}
Con este podemos hacer cast de nuestra clase a un double . Es nuestro problema decidir cómo se
hace ese cast. En el código de ejemplo que hay más abajo se ha definido como la operación módulo
del número complejo.
double a;
ComplejoC b;
a = (double)b;
En el operator cast se pone operator seguido del tipo al que se quiere hacer el cast. No se pone el
tipo del valor devuelto, puesto que ya está claro. Si ponemos operator double, hay que devolver un
double .
En el operator cast no se pone parámetro, puesto que el parámetro recibido será una instancia de
la clase.
Conviene tener cuidado con definir muchos operadores cast, puesto que el compilador los tendrá
todos presentes y será capaz, encadenando unos con otros, de hacer cast entre tipos que no tienen
nada que ver. Por ejemplo, si sumamos un ComplejoC con un DibujoC (que no tienen nada que ver)
y ambos tienen cast e int, es posible que el compilador los transforme ambos en int y luego los sume
como enteros.
¿Cómo hacemos un cast al revés?. Es decir, ¿Cómo podemos convertir un double a ComplejoC?.
El asunto es sencillo, basta hacer un constructor que admite un double como parámetro.

class ComplejoC
{
public:
ComplejoC (double valor);
};
Este constructor sirve para lo que ya sabemos
ComplejoC a(3.0);
o podemos usarlo para hacer un cast de double a ComplejoC

ComplejoC a;
a = (ComplejoC)3.0;
El operador igual
El operator = es como los demás. Simplemente un pequeño detalle. C++ por defecto tiene el
operador igual definido para clases del mismo tipo. Por ejemplo, sin necesidad de redefinir nada,
podemos hacer

ComplejoC a;
ComplejoC b;
a = b;
Este igual por defecto lo único que hace es copiar el contenido del uno sobre el otro, como si fueran
bytes, sin saber qué atributos está copiando ni qué significan. Para clases sencillas, que solo tienen
atributos que no son punteros, esto es más que suficiente.
Si embargo, si algún atributo es un puntero, tenemos que tener mucho cuidado con lo que hacemos.
Supongamos que ClaseC tiene un atributo Atributo que es un puntero. Supongamos también que
en el constructor de la clase, se hace new del puntero para que tenga algo y en el destructor se hace
el delete correspondiente.

ClaseC
{
public:
// Se crea un array de tres enteros
ClaseC () {
Atributo = new int[3];
}
// Se libera el array.
~ClaseC () {
delete [] Atributo;
}
protected:
int *Atributo;
};
Si ahora hacemos esto
// a tiene ahora un array de 3 enteros en su interior
ClaseC *a = new ClaseC();
// b tiena ahora otro array de 3 enteros en su interior
ClaseC *b = new ClaseC();
/* Se copia el Atributo de b sobre el de a, es decir, ahora a->Atributo apunta al mismo sitio que b-
>Atributo */
*a = *b;
// Ahora si que la hemos liado.
delete b;
Cuando hacemos a=b, con el operador igual por defecto de C++, se hace que el puntero Atributo
de a apunte al mismo sitio que el de b. El array original de a->Atributo lo hemos perdido, sigue
ocupando memoria y no tenemos ningún puntero a él para liberarlo.
Cuando hacemos delete b, el destructor de b se encarga de liberar su array. Sin embargo, al puntero
a->Atributo nadie le avisa de esta liberación, se queda apuntando a una zona de memoria que ya
no es válida. Cuando intentemos usar a->Atributo más adelante, puede pasar cualquier cosa
(cambios aleatorios de variables, caidas del programa, etc).
En el tema de punteros tienes un poco más detallado todo esto. Allí se habla de estructuras, pero
también se aplica a clases.
La forma de solucionar esto, es definiendo nosotros un operator = que haga una copia real del array,
liberando previamente el nuestro o copiando encima los datos.

ClaseC
{
public:
ClaseC &operator = (const ClaseC &original)
{
int i;
/* Damos por supuesto que ambos arrays existen y son de tamaño 3 */
for (i=0;i<3;i++)
Atributo[i] = original.Atributo[i];
}
};

Sobrecarga del operador new y delete
Otro operador interesante de sobrecargar es el new y el delete. Si los sobrecargamos dentro de la
clase, cada vez que hagamos un new a nuestra clase se llamará a nuestro operador.
Más interesante es la sobrecarga de los operadores new y delete globales. Sobrecargando estos
operadores se llamará a nuestras funciones cada vez que hagamos un new o un delete de cualquier
cosa (clases o variables). Esta característica nos permite hacer contabilidad de punteros, para ver si
liberamos todo lo que reservamos o liberamos lo mismo más veces de la cuenta.

Más contenido relacionado

La actualidad más candente

Diferencia entre algoritmo de path finding y algoritmo de dijkstra
Diferencia entre algoritmo de path finding y algoritmo de dijkstraDiferencia entre algoritmo de path finding y algoritmo de dijkstra
Diferencia entre algoritmo de path finding y algoritmo de dijkstranacasma
 
Constructores & excepciones
Constructores & excepcionesConstructores & excepciones
Constructores & excepcionesJuan Mancilla
 
Abstract Class & Abstract Method in Core Java
Abstract Class & Abstract Method in Core JavaAbstract Class & Abstract Method in Core Java
Abstract Class & Abstract Method in Core JavaMOHIT AGARWAL
 
[OOP - Lec 19] Static Member Functions
[OOP - Lec 19] Static Member Functions[OOP - Lec 19] Static Member Functions
[OOP - Lec 19] Static Member FunctionsMuhammad Hammad Waseem
 
Types of exceptions
Types of exceptionsTypes of exceptions
Types of exceptionsmyrajendra
 
Programación 3: listas y conjuntos en java
Programación 3: listas y conjuntos en javaProgramación 3: listas y conjuntos en java
Programación 3: listas y conjuntos en javaAngel Vázquez Patiño
 
Java Course 8: I/O, Files and Streams
Java Course 8: I/O, Files and StreamsJava Course 8: I/O, Files and Streams
Java Course 8: I/O, Files and StreamsAnton Keks
 
Estructura de Datos - Unidad 4 Estructuras no lineales
Estructura de Datos - Unidad 4 Estructuras no linealesEstructura de Datos - Unidad 4 Estructuras no lineales
Estructura de Datos - Unidad 4 Estructuras no linealesJosé Antonio Sandoval Acosta
 
Heap sort (Monticulos)
Heap sort (Monticulos)Heap sort (Monticulos)
Heap sort (Monticulos)leidy2220
 
9. Input Output in java
9. Input Output in java9. Input Output in java
9. Input Output in javaNilesh Dalvi
 
06. operator overloading
06. operator overloading06. operator overloading
06. operator overloadingHaresh Jaiswal
 
Comparable and comparator – a detailed discussion
Comparable and comparator – a detailed discussionComparable and comparator – a detailed discussion
Comparable and comparator – a detailed discussionDharmendra Prasad
 

La actualidad más candente (20)

Diferencia entre algoritmo de path finding y algoritmo de dijkstra
Diferencia entre algoritmo de path finding y algoritmo de dijkstraDiferencia entre algoritmo de path finding y algoritmo de dijkstra
Diferencia entre algoritmo de path finding y algoritmo de dijkstra
 
Constructores & excepciones
Constructores & excepcionesConstructores & excepciones
Constructores & excepciones
 
Tipos de listas en estructura de datos
Tipos de listas en estructura de datosTipos de listas en estructura de datos
Tipos de listas en estructura de datos
 
Abstract Class & Abstract Method in Core Java
Abstract Class & Abstract Method in Core JavaAbstract Class & Abstract Method in Core Java
Abstract Class & Abstract Method in Core Java
 
String, string builder, string buffer
String, string builder, string bufferString, string builder, string buffer
String, string builder, string buffer
 
[OOP - Lec 19] Static Member Functions
[OOP - Lec 19] Static Member Functions[OOP - Lec 19] Static Member Functions
[OOP - Lec 19] Static Member Functions
 
Types of exceptions
Types of exceptionsTypes of exceptions
Types of exceptions
 
VBA Interfaz de usuario
VBA Interfaz de usuarioVBA Interfaz de usuario
VBA Interfaz de usuario
 
Programación 3: listas y conjuntos en java
Programación 3: listas y conjuntos en javaProgramación 3: listas y conjuntos en java
Programación 3: listas y conjuntos en java
 
Java Course 8: I/O, Files and Streams
Java Course 8: I/O, Files and StreamsJava Course 8: I/O, Files and Streams
Java Course 8: I/O, Files and Streams
 
File Handling In C++
File Handling In C++File Handling In C++
File Handling In C++
 
Estructura de Datos - Unidad 4 Estructuras no lineales
Estructura de Datos - Unidad 4 Estructuras no linealesEstructura de Datos - Unidad 4 Estructuras no lineales
Estructura de Datos - Unidad 4 Estructuras no lineales
 
Algoritmos de Ordenamiento externo
Algoritmos de Ordenamiento externoAlgoritmos de Ordenamiento externo
Algoritmos de Ordenamiento externo
 
Autómatas de pila
Autómatas de pila Autómatas de pila
Autómatas de pila
 
Heap sort (Monticulos)
Heap sort (Monticulos)Heap sort (Monticulos)
Heap sort (Monticulos)
 
9. Input Output in java
9. Input Output in java9. Input Output in java
9. Input Output in java
 
06. operator overloading
06. operator overloading06. operator overloading
06. operator overloading
 
Java inheritance
Java inheritanceJava inheritance
Java inheritance
 
Comparable and comparator – a detailed discussion
Comparable and comparator – a detailed discussionComparable and comparator – a detailed discussion
Comparable and comparator – a detailed discussion
 
Method overriding
Method overridingMethod overriding
Method overriding
 

Similar a Por qué java no soporta la sobre carga de operadores

Sobrecarga de operadores
Sobrecarga de operadoresSobrecarga de operadores
Sobrecarga de operadoresr0na91
 
Unidad2
Unidad2Unidad2
Unidad2JHONNY
 
Operaciones Básicas
Operaciones BásicasOperaciones Básicas
Operaciones Básicaschembe
 
Lenguaje de programacion C++ 2
Lenguaje de programacion C++ 2Lenguaje de programacion C++ 2
Lenguaje de programacion C++ 2David
 
Lenguajesdeprogramacion c nivel1-unidad2
Lenguajesdeprogramacion c nivel1-unidad2Lenguajesdeprogramacion c nivel1-unidad2
Lenguajesdeprogramacion c nivel1-unidad2camnav
 
Operaciones Basicas C++
Operaciones Basicas C++Operaciones Basicas C++
Operaciones Basicas C++yesid19
 
Lenguajesdeprogramacion c nivel1-unidad2
Lenguajesdeprogramacion c nivel1-unidad2Lenguajesdeprogramacion c nivel1-unidad2
Lenguajesdeprogramacion c nivel1-unidad2Joaquin1418
 
Lenguajes De Programacion C nivel1-unidad2
Lenguajes De Programacion C nivel1-unidad2Lenguajes De Programacion C nivel1-unidad2
Lenguajes De Programacion C nivel1-unidad2charnisch
 
Operaciones Básicas
Operaciones BásicasOperaciones Básicas
Operaciones Básicaschembe
 
Lenguajes de programacion c
Lenguajes de programacion cLenguajes de programacion c
Lenguajes de programacion ccharnisch
 
El lenguaje C++ (1).ppt
El lenguaje C++ (1).pptEl lenguaje C++ (1).ppt
El lenguaje C++ (1).pptJOSECASHUUG
 
Instituto tecnológico particular
Instituto tecnológico particularInstituto tecnológico particular
Instituto tecnológico particularCarlota Guzman
 
Funciones C++
Funciones C++Funciones C++
Funciones C++Jarvis_15
 
Funciones con vectores c++
Funciones con vectores c++Funciones con vectores c++
Funciones con vectores c++jose machado
 
Investigacion sobre carga de metodos
Investigacion sobre carga de metodosInvestigacion sobre carga de metodos
Investigacion sobre carga de metodosArisRojas4
 
Introduccion
IntroduccionIntroduccion
Introduccionreivax
 

Similar a Por qué java no soporta la sobre carga de operadores (20)

Sobrecarga de operadores
Sobrecarga de operadoresSobrecarga de operadores
Sobrecarga de operadores
 
Unidad2
Unidad2Unidad2
Unidad2
 
Operaciones Básicas
Operaciones BásicasOperaciones Básicas
Operaciones Básicas
 
Lenguaje de programacion C++ 2
Lenguaje de programacion C++ 2Lenguaje de programacion C++ 2
Lenguaje de programacion C++ 2
 
Lenguajesdeprogramacion c nivel1-unidad2
Lenguajesdeprogramacion c nivel1-unidad2Lenguajesdeprogramacion c nivel1-unidad2
Lenguajesdeprogramacion c nivel1-unidad2
 
Operaciones Basicas C++
Operaciones Basicas C++Operaciones Basicas C++
Operaciones Basicas C++
 
Lenguajesdeprogramacion c nivel1-unidad2
Lenguajesdeprogramacion c nivel1-unidad2Lenguajesdeprogramacion c nivel1-unidad2
Lenguajesdeprogramacion c nivel1-unidad2
 
Lenguajes De Programacion C nivel1-unidad2
Lenguajes De Programacion C nivel1-unidad2Lenguajes De Programacion C nivel1-unidad2
Lenguajes De Programacion C nivel1-unidad2
 
Operaciones Básicas
Operaciones BásicasOperaciones Básicas
Operaciones Básicas
 
Lenguajes de programacion c
Lenguajes de programacion cLenguajes de programacion c
Lenguajes de programacion c
 
Dotnetmania 9 pag_21_29
Dotnetmania 9 pag_21_29Dotnetmania 9 pag_21_29
Dotnetmania 9 pag_21_29
 
El lenguaje C++ (1).ppt
El lenguaje C++ (1).pptEl lenguaje C++ (1).ppt
El lenguaje C++ (1).ppt
 
El lenguaje C++.ppt
El lenguaje C++.pptEl lenguaje C++.ppt
El lenguaje C++.ppt
 
Primeros Ejemplos Usando Operadores en Visual C# (C Sharp)
Primeros Ejemplos Usando Operadores en Visual C# (C Sharp)Primeros Ejemplos Usando Operadores en Visual C# (C Sharp)
Primeros Ejemplos Usando Operadores en Visual C# (C Sharp)
 
Mod2ud1 2
Mod2ud1 2Mod2ud1 2
Mod2ud1 2
 
Instituto tecnológico particular
Instituto tecnológico particularInstituto tecnológico particular
Instituto tecnológico particular
 
Funciones C++
Funciones C++Funciones C++
Funciones C++
 
Funciones con vectores c++
Funciones con vectores c++Funciones con vectores c++
Funciones con vectores c++
 
Investigacion sobre carga de metodos
Investigacion sobre carga de metodosInvestigacion sobre carga de metodos
Investigacion sobre carga de metodos
 
Introduccion
IntroduccionIntroduccion
Introduccion
 

Más de Manuel Alejandro Hernandez Sanchez

Más de Manuel Alejandro Hernandez Sanchez (20)

Serialización personalizada
Serialización personalizadaSerialización personalizada
Serialización personalizada
 
Ejemplo
EjemploEjemplo
Ejemplo
 
Entrada
EntradaEntrada
Entrada
 
Ejemplo
EjemploEjemplo
Ejemplo
 
Tipos de excepciones
Tipos de excepcionesTipos de excepciones
Tipos de excepciones
 
Introducción
IntroducciónIntroducción
Introducción
 
Herencia
HerenciaHerencia
Herencia
 
Tipo estático y tipo dinámico
Tipo estático y tipo dinámicoTipo estático y tipo dinámico
Tipo estático y tipo dinámico
 
Introducción
IntroducciónIntroducción
Introducción
 
Uso de polimorfismo
Uso de polimorfismoUso de polimorfismo
Uso de polimorfismo
 
Enlace dinámico
Enlace dinámicoEnlace dinámico
Enlace dinámico
 
Clase base
Clase baseClase base
Clase base
 
Sobrecarga de constructores
Sobrecarga de constructoresSobrecarga de constructores
Sobrecarga de constructores
 
Recolección de basura
Recolección de basuraRecolección de basura
Recolección de basura
 
Retorno de valores
Retorno de valoresRetorno de valores
Retorno de valores
 
Metodos de mensajes
Metodos de mensajesMetodos de mensajes
Metodos de mensajes
 
La destrucción del objeto
La destrucción del objetoLa destrucción del objeto
La destrucción del objeto
 
Acceso al objeto
Acceso al objetoAcceso al objeto
Acceso al objeto
 
Agregar un metodo que toma parametros
Agregar un metodo que toma parametrosAgregar un metodo que toma parametros
Agregar un metodo que toma parametros
 
Asignación de variables de referencia de objeto
Asignación de variables de referencia de objetoAsignación de variables de referencia de objeto
Asignación de variables de referencia de objeto
 

Por qué java no soporta la sobre carga de operadores

  • 1. ¿Por qué java no soporta la sobre carga de operadores? Java no soporta sobrecarga de operadores, aunque el sistema internamente la utiliza, pero está ocultada al programador, por ejemplo si te fijas al hacer un Int ejemplo=2+1; En java eso es válido y le asigna un 3 a la variable ejemplo la funcionalidad fue sumar, sin embargo en este: String ejemplo="hola"+"mundo"; El operador + se utiliza con otra finalidad : Concatenar , lo ves? Java si usa la sobrecarga de operadores, pero esta oculta para el programador, ósea no te permite usarla. ¿Por qué? Básicamente para no llevar a confusión , imagina que defines una clase llamada "sobreEscribe" en la cual sobrecargaras el operador suma y le asignaras tu propia forma de trabajar por ejemplo sumar fracciones y más tarde defines la clase "fracciones" heredando la clase "sobreEscribe" el resultado sería que en la clase "fracciones" el operador suma cambiara su comportamiento. Si llegase ahora un programador externo y revisara la clase fracciones podría confundirse y creer que este operador mantiene el comportamiento normal, para saber que tiene el comportamiento actual tendría que ir a revisar "sobreEscribe", ahora imagina que esta herencia se realiza entre varias clases, sería muy difícil saber si realmente se está cambiando el comportamiento del operador suma o no. Esta es una de las razones más fuertes por las cuales Java no soporta la sobrecarga de operadores básicamente para no llevar a confusión y al fin y al cabo no es necesaria, puedes utilizar tus propios métodos ¿Qué es la sobrecarga de operadores? En C++ se pueden hacer operaciones con los tipos básicos del lenguaje: se pueden sumar enteros, compararlos, etc. Lo siguiente es perfectamente válido en C++ int a=3; int b=4; int c; c = a + b; Si estos tipos no son los básicos del lenguaje, no se puede hacer sumas con ellos. Por ejemplo, si ClaseC es una clase que tengo definida en C++, el siguiente código dará error. ClaseC a; ClaseC b; ClaseC c;
  • 2. c = a + b; C++ permite que hagamos estas cosas si definimos en algún sitio cómo se suman esas clases. Definiendo cómo se hacen las operaciones, podremos escribir las operaciones con nuestras clases de la misma forma que si se trataran de tipos básicos del lenguaje. De hecho podemos definir cualquier operador que nos dé C++, desde algunos muy normales como suma, resta, multiplicación, mayor qué, etc, a otros un poco más raros como el operador () o el [], de forma que ClaseC[i] o ClaseC(i,j) pueden devolver lo que nosostros queramos. Sobrecarga del operador suma Si tenemos una clase ComplejoC que representa un complejo, para poder sumar dos de estas clases simplemente poniendo un +, como con cualquier tipo básico de C++, debemos sobrecargar el operador +, darle una nueva funcionalidad. La sobrecarga de un operador suma se hace de la siguiente manera class ComplejoC { public: // Permite sumar un ComplejoC con un double ComplejoC operator + (double sumando); // Permite sumar un ComplejoC con un array ComplejoC operator + (const double sumando[]); // Permite sumar dos ComplejoC entre sí. ComplejoC operator + (const ComplejoC &sumando); }; Aquí estamos redefiniendo tres operadores suma para que nos permita sumar a nuestra clase ComplejoC cosas como un double, un array de double (que supondremos contiene dos double, aunque no tenemos forma de comprobarlo) y otra clase ComplejoC . En estas funciones hemos puesto const en los parámetros para no poder cambiarlos dentro del código. En la tercera función pasamos ComplejoC por referencia (el &), para evitar que se hagan copias innecesarias. En la primera función no es necesario nada de esto, puesto que es un simple
  • 3. double. En la segunda, ponemos const para no poder modificar el array, pero no es necesaria la referencia, puesto que los arrays son punteros. A la hora de implementar debemos tener cuidado con los const que hemos puesto. Por ejemplo, en el tercer operador +, recibimos un sumando que es const. El código que implementemos dentro no puede modificar dicho sumando, ni puede llamar a ningún método de ese sumando que no esté declarado como const. Si lo intentamos, el compilador dará error. Supongamos que ComplejoC tiene un atributo double x (parte real) y un método dameX() para obtener dicho atributo, este método debe estar declarado const para poder llamarlo desde nuestro operator +. Más o menos esto: class ComplejoC { public: // Atención al const del final. Sin el no podemos llamar a sumando.dameX(). double dameX() const; }; De forma que en el operator + podemos llamar a sumando.dameX(). Una vez implementados estos métodos, podemos hacer operaciones como ComplejoC a; ComplejoC b; // Aprovechando la primera sobrecarga b = a + 1.0; // Aprovechando la segunda sobrecarga double c[] = {1.0, 2.0}; b = a + c; // Aprovechando la tercera sobrecarga b = a + b; Cuando el compilador lee a + 1.0, lo interpreta como a.operator + (1.0), es decir, la llamada al operador suma al que se le pasa como parámetro un double . De la misma forma sucede con los otros dos operadores suma. Sin embargo, esto nos da un pequeño problema. ¿Qué pasa si ponemos 1.0 + a?. Debería ser lo mismo, pero al compilador le da un pequeño problema. Intenta llamar al método operator + de 1.0, que no existe, puesto que 1.0 ni es una clase ni tiene métodos. Para solucionar este problema tenemos la sobrecarga de operadores globales.
  • 4. Sobrecarga de operadores suma globales Un operador global es una función global que no pertenece a ninguna clase y que nos indica cómo operar con uno o dos parámetros (depende del tipo de operador). En nuestro ejemplo de las sumas, para poder poner los sumandos al revés, deberíamos definir las siguientes funciones globales: ComplejoC operator + (double sumando1, const ComplejoC &sumando2); ComplejoC operator + (double sumando1[], const ComplejoC &sumando2); Estas funciones le indican al compilador cómo debe sumar los dos parámetros y qué devuelve. Con ellas definidas e implementadas, ya podemos hacer b = 1.0 + a; // c era un array de double b = c + a; Esta sobrecarga es especialmente útil cuando tratamos con una clase ya hecha y que no podemos modificar. Por ejemplo, cout es de la clase ostream y no podemos modificarla, sin embargo nos sería de utilidad sobrecargar el operador << de ostream de forma que pueda escribir nuestros números complejos. La siguiente llamada nos dará error mientras no redefinamos el operator << de ostream . cout << a << endl; // a es un ComplejoC Con la sobrecarga de operadores globales podemos definir la función ostream &operator << (ostream &salida, const ComplejoC &valor); Con esta función definida, el complejo se escribirá en pantalla como indique dicha función. Esta función deberá escribir la parte real e imaginaria del complejo en algún formato, utilizando algo como salida << valor.dameX() << " + " << valor.dameY() << "j"; El operador devuelve un ostream, que será un return cout. De esta forma se pueden encadenar las llamadas a cout de la forma habitual. cout << a << " + " << b << endl;
  • 5. Primero se evalúa operator << (cout, a), que escribe a en pantalla y devuelve un cout, con lo que la expresión anterior quedaría, después de evaluar esto cout << " + " << b << endl; Y así consecutivamente. Hay que tener en cuenta que estos operadores globales no son de la clase, así que sólo pueden acceder a métodos y atributos públicos de la misma. El operador cast Un operador interesante es el operador "cast". En C++, si tenemos dos tipos básicos distintos, podemos pasar de uno a otro haciendo un cast (siempre que sean compatibles de alguna forma). Por ejemplo int a; double b; a = (int)b; El cast consiste en poner delante, entre paréntesis, el tipo que queramos que sea. En algunos casos, como en el de este ejemplo,el cast se hace automáticamente y no es necesario ponerlo. Puede que de un "warning" en el compilador avisando de que perderemos los decimales. En principio, con las clases no se puede hacer cast a otros tipos, pero es posible declarar operadores que lo hagan. La sintaxis sería: class ComplejoC { public: // Permite hacer un cast de ComplejoC a double operator double (); } Con este podemos hacer cast de nuestra clase a un double . Es nuestro problema decidir cómo se hace ese cast. En el código de ejemplo que hay más abajo se ha definido como la operación módulo del número complejo.
  • 6. double a; ComplejoC b; a = (double)b; En el operator cast se pone operator seguido del tipo al que se quiere hacer el cast. No se pone el tipo del valor devuelto, puesto que ya está claro. Si ponemos operator double, hay que devolver un double . En el operator cast no se pone parámetro, puesto que el parámetro recibido será una instancia de la clase. Conviene tener cuidado con definir muchos operadores cast, puesto que el compilador los tendrá todos presentes y será capaz, encadenando unos con otros, de hacer cast entre tipos que no tienen nada que ver. Por ejemplo, si sumamos un ComplejoC con un DibujoC (que no tienen nada que ver) y ambos tienen cast e int, es posible que el compilador los transforme ambos en int y luego los sume como enteros. ¿Cómo hacemos un cast al revés?. Es decir, ¿Cómo podemos convertir un double a ComplejoC?. El asunto es sencillo, basta hacer un constructor que admite un double como parámetro. class ComplejoC { public: ComplejoC (double valor); }; Este constructor sirve para lo que ya sabemos ComplejoC a(3.0); o podemos usarlo para hacer un cast de double a ComplejoC ComplejoC a; a = (ComplejoC)3.0;
  • 7. El operador igual El operator = es como los demás. Simplemente un pequeño detalle. C++ por defecto tiene el operador igual definido para clases del mismo tipo. Por ejemplo, sin necesidad de redefinir nada, podemos hacer ComplejoC a; ComplejoC b; a = b; Este igual por defecto lo único que hace es copiar el contenido del uno sobre el otro, como si fueran bytes, sin saber qué atributos está copiando ni qué significan. Para clases sencillas, que solo tienen atributos que no son punteros, esto es más que suficiente. Si embargo, si algún atributo es un puntero, tenemos que tener mucho cuidado con lo que hacemos. Supongamos que ClaseC tiene un atributo Atributo que es un puntero. Supongamos también que en el constructor de la clase, se hace new del puntero para que tenga algo y en el destructor se hace el delete correspondiente. ClaseC { public: // Se crea un array de tres enteros ClaseC () { Atributo = new int[3]; } // Se libera el array. ~ClaseC () { delete [] Atributo; } protected: int *Atributo; }; Si ahora hacemos esto // a tiene ahora un array de 3 enteros en su interior ClaseC *a = new ClaseC(); // b tiena ahora otro array de 3 enteros en su interior ClaseC *b = new ClaseC(); /* Se copia el Atributo de b sobre el de a, es decir, ahora a->Atributo apunta al mismo sitio que b-
  • 8. >Atributo */ *a = *b; // Ahora si que la hemos liado. delete b; Cuando hacemos a=b, con el operador igual por defecto de C++, se hace que el puntero Atributo de a apunte al mismo sitio que el de b. El array original de a->Atributo lo hemos perdido, sigue ocupando memoria y no tenemos ningún puntero a él para liberarlo. Cuando hacemos delete b, el destructor de b se encarga de liberar su array. Sin embargo, al puntero a->Atributo nadie le avisa de esta liberación, se queda apuntando a una zona de memoria que ya no es válida. Cuando intentemos usar a->Atributo más adelante, puede pasar cualquier cosa (cambios aleatorios de variables, caidas del programa, etc). En el tema de punteros tienes un poco más detallado todo esto. Allí se habla de estructuras, pero también se aplica a clases. La forma de solucionar esto, es definiendo nosotros un operator = que haga una copia real del array, liberando previamente el nuestro o copiando encima los datos. ClaseC { public: ClaseC &operator = (const ClaseC &original) { int i; /* Damos por supuesto que ambos arrays existen y son de tamaño 3 */ for (i=0;i<3;i++) Atributo[i] = original.Atributo[i]; } }; Sobrecarga del operador new y delete Otro operador interesante de sobrecargar es el new y el delete. Si los sobrecargamos dentro de la clase, cada vez que hagamos un new a nuestra clase se llamará a nuestro operador. Más interesante es la sobrecarga de los operadores new y delete globales. Sobrecargando estos operadores se llamará a nuestras funciones cada vez que hagamos un new o un delete de cualquier
  • 9. cosa (clases o variables). Esta característica nos permite hacer contabilidad de punteros, para ver si liberamos todo lo que reservamos o liberamos lo mismo más veces de la cuenta.