Fecha y hora actual: Viernes 18 Oct 2019 13:26
Índice del Foro

Foros de programación informática, diseño gráfico y Web

En esta comunidad intentaremos dar soporte de programación a todos los niveles, desde principiantes a profesionales de la informática, desarrollo de programas, programación web y mucho más.

Lección 67: Operaciones en Java - Introducción a la POO.

Responder al Tema

Índice del Foro > Programación en general > Lección 67: Operaciones en Java - Introducción a la POO.

Autor Mensaje
Kyshuo Ayame
Moderador Global


Registrado: 07 Ene 2011
Mensajes: 1044

Mensaje Publicado: Viernes 17 Ago 2012 19:31

Título del mensaje: Lección 67: Operaciones en Java - Introducción a la POO.

Responder citando

La mejor manera, según mi criterio, de introducir las operaciones en Java es creando algún tipo de objeto que las utilice. Esto es porque si lo hago directamente sobre la clase principal, tendríamos que usar el modificador static para crear operaciones utilizables en el método main, pero es difícil entender eso del static si aún no se han creado objetos. Así que vayamos a ello.

Haremos lo siguiente:

Crearemos un nuevo proyecto llamado DatosPersonas. Crearemos una clase principal llamada también DatosPersonas la cual tendrá por supuesto el procedimiento main. Luego crearemos otra clase llamada Persona que definirá los objetos del tipo Persona. Trabajaremos sobre dicha clase hasta aprender todos los conceptos necesarios para que quede idéntica al módulo Persona de nuestro pequeño sistema en Modula 2 al cual habíamos llamado NominaPersonas.

Bien, comencemos poco a poco:

  • Creamos el nuevo proyecto tal como hemos hecho hasta ahora.
  • Creamos una clase y la llamamos DatosPersona. Su código fuente será el mínimo indispensable para que sea reconocida como clase principal, es decir

    Código:
    1. public class DatosPersonas {
    2. public static void main(String[] args){
    3.  
    4. }
    5. }


  • Creamos una clase llamada Persona. Su código fuente será simplemente este:

    Código:
    1. public class Persona{
    2.  
    3. }


    Es decir que solo está declarada como clase pública y nada más. Recuerden que para declarar una clase simplemente va la palabra class, luego el identificador y las llaves de apertura { y cierre } que forman el cuerpo de la clase. En este caso agregamos además el modificador public para que la clase sea accesible desde otros módulos del programa.



En Modula teníamos aquello de archivo de definición y archivo de implementación. Como dije antes, en Java todo está en el mismo archivo, es decir, en la clase. Las clases definen por sí mismas tipos de objetos que podemos crear con ellas. De este modo, al definir una clase Persona, ya estamos definiendo una futura creación de objetos de tipo Persona.
En Modula teníamos que definir en el archivo de definición que íbamos a usar un tipo de objetos de ese módulo. Eso lo hacíamos con los tipos opacos. Ahora olvidamos ese aspecto porque la propia declaración de la clase define que se crearán objetos de ese tipo.
Luego, en Módula debíamos ir al archivo de implementación y dar una representación para el tipo de objetos que íbamos a crear. Esto lo hacíamos con un puntero a un registro que definíamos en el mismo módulo. Por ejemplo, el tipo Persona del sistema NominaPersonas era así:

Código:
  1. TYPE
  2. Persona= POINTER TO DatosPersona
  3.  
  4. DatosPersona= RECORD
  5. Nombre, Apellido: TipoNombre;
  6. Edad, Documento: CARDINAL;
  7. SueldoPorHora: REAL;
  8. HorasTrabajadas: REAL;
  9. END;
  10.  

Antes dije que en Java no podemos manejar punteros y también dije que no tenemos registros. Así que esa definición de arriba solo nos servirá como modelo. Tomemos de ese tipo simplemente los datos Nombre y Apellido, es decir, de momento nuestros objetos Persona tendrán solo un nombre y un apellido. ¿Cómo definimos eso en Java? Pues fácil, declaramos dos variables para esos datos y ya:

Código:
  1. public class Persona {
  2. public String nombre;
  3. public String apellido;
  4. }


Como ven solo declare dos variables del tipo String, pero… ¿qué es eso de public en las variables?
Esa palabra la han visto ya al declarar la clase. Se trata de un modificador de acceso; en este caso quiere decir que esas variables son públicas de la clase Persona, es decir, podemos acceder a ellas desde otra clase cualquiera. Es casi como si fueran variables declaradas en el DEF de un módulo de Modula 2. Vamos un ejemplo:

Ya tenemos entonces nuestra clase Persona declarada la cual tiene dos variables públicas para almacenar un nombre y un apellido. Vamos entonces a la clase principal y nos declaramos una variable llamada p del tipo Persona:

Código:
  1. public class DatosPersonas {
  2. public static void main(String[] args){
  3. Persona p;
  4.  
  5. }
  6. }


Sabiendo ustedes que p entonces será un objeto de tipo Persona, deberían ya deducir entonces que p en realidad es una referencia a un objeto de tipo Persona. La variable p contendrá la dirección de memoria en donde estará el objeto Persona con su nombre y su apellido.
Ahora p está simplemente declarada por lo cual no sabemos qué valor posee, incluso si quisiéramos usarla ahora NetBeans no daría un error de sintaxis porque la variable no está inicializada.

¿Cómo la inicializo? Pues en Modula teníamos una operación constructora llamada CrearPersona, la cual se encargaba de solicitar memoria y apuntar al puntero a dicho lugar de memoria. Esta operación además se encargaba de asignar los valores de los atributos ya que los recibía como argumentos. Recordémosla:

Código:
  1. PROCEDURE CrearPersona(documento: INTEGER; nombre, apellido: TipoNombre; edad: INTEGER; sueldo, horas: REAL): Persona;


En Java también necesitamos una operación constructora, sin embargo, como no la hemos definido Java nos da una por defecto la cual recibe el mismo nombre de la clase y no requiere argumentos. Las operaciones constructoras en Java siempre se usan mediante el operador new. De este modo, para inicializar la variable p hacemos

Código:
  1. p= new Persona();


El operador new, al igual que en Pascal y Modula solicita memoria para almacenar un objeto y la reserva. Mediante la operación constructora se encarga, luego de haber pedido memoria, de inicializar los atributos del objeto retornando finalmente la dirección de memoria hacia él. A nosotros no nos importa la dirección de memoria ni cómo se escribe, nos importa que p es una referencia a un objeto y ya; o sea, seguiremos viendo a p como una flecha hacia una entidad de memoria que contiene muchos datos manipulables.

¿Cómo sabe la operación constructora cómo inicializar los atributos del objeto? Seguramente ahora eso de una operación constructora por defecto suena raro. Pues bien, como no hemos escrito nada sobre esta operación, sino que nos apegamos a la que Java nos da, debemos saber que los atributos se inicializarán entonces con valores predeterminados:

  • Variables de tipos primitivos se inicializan en 0 o en FALSE si son booleanos.
  • Variables que son referencias a objetos se inicializan en null (lo veremos luego).


En este caso, nuestra clase Persona tiene solo dos atributos: nombre y apellido. Estos atributos son del tipo String, por tanto no son tipos primitivos sino que son referencias a objetos así que se inicializarán en null. Esto significa que son Strings nulos, lo cual no es lo mismo que Strings vacíos representados por el valor “”.

-------------------------------------------------------------------------------------

El valor null:

Así como yo podía definir en Pascal y Modula un puntero que no apuntara a nada asignándole el valor NIL, en Java puedo hacerlo también. Sabiendo que las referencias a objetos son punteros a esos objetos, puedo asignar a un puntero el valor NIL, solo que en Java se llama null. Por tanto yo puedo hacer algo como esto:

Código:
  1. p= null;


Como null es un valor aplicable a cualquier variable puntero, es decir, a cualquier referencia a objetos, puedo preguntar sobre su valor comparando:


Código:
  1. if (p==null)
  2. . . .


Esto es igual que en Pascal y Modula.

------------------------------------------------------------------------------------


Accediendo a atributos públicos:

Bien, continuando con el ejemplo, tenemos en nuestra clase principal un objeto p del tipo Persona el cual sabemos fue inicializado con valores por defecto de modo que sus únicos dos atributos son nulos.

Completemos el ejemplo asignando un nombre y un apellido al objeto p accediendo directamente a su representación para luego imprimir eso en la salida estándar. Veamos el código fuente de la clase principal completo:

Código:
  1. public class DatosPersonas {
  2. public static void main(String[] args){
  3. Persona p= new Persona();
  4.  
  5. p.nombre= "Kyshuo";
  6. p.apellido= "Ayame";
  7.  
  8. System.out.println("Nombre: " + p.nombre + "\nApellido: " + p.apellido);
  9. }
  10. }


Como pueden ver, para acceder a las variables del objeto simplemente ponemos el nombre de la referencia, un punto y luego el nombre de la variable. Es más, si ustedes escriben el nombre de la referencia al objeto de tipo Persona y un punto verán que NetBeans despliega un listado de lo que tenemos disponible. Allí verán las variables que definimos y luego alguna otra cosa más que de momento no sabemos de dónde sale. Ya investigaremos sobre el tema.

Si al escribir una referencia a un objeto seguida de un punto no ven el listado de opciones o bien, NetBeans lo cerró, simplemente presionen ALT Gr + ESPACIO y lo tendrán a su disposición. Resulta muy útil, sobre todo cuando tengamos muchas clases con muchas variables y demás.

De este modo hemos accedido directamente a las variables (atributos) de la clase Persona. Esto únicamente fue posible porque estas variables fueron declaradas como públicas y por tanto podemos acceder a ellas desde otra clase que no sea la clase Persona.

-------------------------------------------------------------------------------------

Modificador de acceso private y operaciones en Java:

Lo que hicimos entonces fue definir una clase Persona que declara únicamente dos atributos: nombre y apellido. Estos atributos fueron declarados como públicos y por tanto desde la clase DatosPersonas pudimos acceder a ellos mediante un objeto de la clase Persona referenciado por una variable p. A estas alturas del partido ustedes deberían estar de acuerdo conmigo en que dejar que los usuarios de una clase accedan directamente a sus atributos (representación) de los datos no es la mejor opción. ¿Qué pasa si a mí se me ocurre otra implementación para la clase Persona donde nombre y apellido ya no sean dos variables distintas del tipo String? Veamos un ejemplo:

Código:
  1. public class Persona {
  2. public String[] nomApell= new String[2];
  3.  
  4. }


Ahora ya no tenemos dos variables String sino que tenemos un arreglo de Strings de dos celdas. La primera celda contendrá el nombre y la segunda contendrá el apellido. Si un usuario pretende utilizar mi nueva clase Persona tendrá que, además de sustituir su antigua clase Persona, modificar el código fuente de su clase principal porque ya no hay una variable nombre y una variable apellido. Si ustedes cambian el código fuente en sus PCs y compilan verán que NetBeans les marcará muchos errores.
Para solucionarlos pueden, o bien dejar la clase Persona como estaba, o bien modificar la clase principal. En este caso no sería un trabajo arduo, pero en un sistema grande ya pueden imaginar que sí.

Esto muestra en un ejemplo práctico que declarar los atributos de una clase como públicos hace que se pierda individualidad en el código, de modo que un cambio en una clase impacta directamente sobre el código fuente de otras. Esto no es para nada lo que queremos. Por eso ya Modula ocultaba la representación del tipo de objetos que implementábamos y nos valíamos de las operaciones para manipular la información.

Volvamos entonces a tener nuestro atributo nombre y nuestro atributo apellido pero ahora declarados como private y no como public:

Código:
  1. public class Persona {
  2. private String nombre;
  3. private String apellido;
  4.  
  5. }


Podemos declarar ambas variables en una sola línea así ¿verdad?

Código:
  1. private String nombre, apellido;


NetBeans nos marcará errores aún porque ahora no podemos acceder a estos atributos desde fuera de la clase Persona porque son privados de ella, es decir, no son visibles para otras clases, ni siquiera la principal. Crearemos entonces una operación ModificarNombrePersona y una ModificarApellidoPersona que permitan justamente dar un nombre y un apellido a un objeto Persona.


Declaración de operaciones:

En Java toda operación se declara de la misma manera sin importar si es función o procedimiento:

Código:
tipoDeRetorno identificador (argumentos)


Para el módulo Persona teníamos una operación ModificarNombrePersona que tenía esta firma:

Código:
  1. PROCEDRUE ModificarNombrePersona(nombre: TipoNombre; VAR p: Persona);


En Java sería así:

Código:
  1. void ModificarNombrePersona(String nom);


Esto es una operación llamada ModificarNombrePersona que retorna algo del tipo void y recibe como argumento algo de tipo String en un parámetro llamado nom. En Java el tipo void es un tipo nulo, es decir que, una operación que retorna void justamente no retorna nada y por eso sabemos que es un procedimiento. Noten que para los parámetros va primero el tipo y luego el identificador, tal como es para las variables.

¿Y qué pasó con el parámetro por referencia que en Modula era justamente el objeto Persona cuyo nombre modificaríamos? Pues ya veremos que esto no es necesario en Java, además de que ya no existe el pasaje de parámetros por referencia.

La operación ModificarNombrePersona quedaría entonces así:

Código:
  1. void ModificarNombrePersona(String nom){
  2. nombre= nom;
  3. }


¿Cómo sabe Java a qué objeto Persona modificar el nombre? Pues, ¿qué mejor manera que verlo con un ejemplo? Veamos, desde la clase principal, teniendo dos objetos Persona distintos, cómo asignamos un nombre a cada uno:

Código:
  1. public class DatosPersonas {
  2. public static void main(String[] args){
  3. Persona p1= new Persona();//Creo un objeto Persona y lo referencio con p1.
  4. Persona p2= new Persona();//Creo un objeto Persona y lo referencio con p2.
  5.  
  6. p1.ModificarNombrePersona("Kyshuo"); //Le asigno nombre a p1.
  7. p2.ModificarNombrePersona("Pepe"); //Le asigno nombre a p2.
  8.  
  9. }
  10. }


Como pueden observar, la operación ModificarNombrePersona, al ser una operación de la clase Persona, se hace a través de un objeto de esa clase. De este modo, al escribir

Código:
  1. p1.ModificarNombrePersona(“Kyshuo”);


queda totalmente explícito que lo haremos con el objeto p1. Las operaciones que se hacen a través de un objeto de una clase siempre se realizan con esta sintaxis:

Código:
  1. referenciaDelObjeto.operacionARealizar(argumentos);


Al tener que, obligatoriamente, anteponer la referencia a un objeto antes del nombre de la operación (separados por un punto) es como si estuviéramos pasando ese objeto como argumento. Por eso no necesitamos explicitarlo como un argumento en la lista de parámetros de la operación como teníamos que hacer en Modula. Esto es algo propio de Java. Para dejarlo claro, en Módula debíamos pasar por referencia un objeto del tipo en cuestión como sucedía con esta operación

Código:
  1. PROCEDRUE ModificarNombrePersona(nombre: TipoNombre; VAR p: Persona);


en Java esto ya no es así. Quitamos el parámetro “objeto” porque quedará explícito al llamar a la operación. Si en Modula teníamos un objeto de tipo Persona referenciado por p1, debíamos llamar a esa operación así:

Código:
  1. ModificarNombrePersona(“Kyshuo”,p1);


En Java quedó así:

Código:
  1. p1.ModificarNombrePersona(“Kyshuo”);


Esto traerá algunas cositas interesantes a tener en cuenta de ahora en más.

-------------------------------------------------------------------------------------

Referencia this:

En Módula, al tener la referencia como nombre de un argumento (en la operación ModificarNombrePersona la habíamos llamado p) nos referíamos a ese parámetro para modificar los valores necesarios. Por ejemplo, en ModificarNombrePersona hacíamos:

Código:
  1. p^.nombre= nombre;


En Java, no tenemos un argumento explícito del tipo en cuestión. ¿Cómo nos referimos entonces al objeto que nos han pasado en el llamado a la operación? Veamos esto con un simple problema:

En el ejemplo anterior llamé al argumento que pasaba el nombre de la persona nom, pero mejor sería llamarlo nombre, de este modo la firma de la operación ModificarNombrePersona sería

Código:
  1. void ModificarNombrePersona(String nombre)


¿Qué pasa si dejo el código anterior?

Código:
  1. void ModificarNombrePersona(String nombre){
  2. nombre= nombre;
  3. }


Dentro de la operación el parámetro nombre funciona como variable local, eso ustedes ya lo tienen clarísimo. Por tanto opaca a la variable nombre global que tenemos declarada como atributo de los objetos Persona, así que la asignación nombre=nombre es asignar al parámetro nombre su propio valor y por tanto la variable global nombre queda intacta. Incluso si ustedes paran el cursor sobre la palabra nombre que usan dentro de la operación ModificarNombrePersona verán que NetBeans les pinta en todo el código a quién están haciendo referencia.

Este problema no lo teníamos en Modula porque al tener el argumento p de tipo Persona hacíamos:

Código:
  1. p^.nombre= nombre;


¿Y si hacemos algo parecido en Java? Pues, como el objeto con el que hacen el llamado a la operación es pasado de forma implícita no tenemos un nombre con qué referenciarlo, pero afortunadamente los creadores de Java tenían esto presente y nos dan una referencia para el objeto en cuestión, la cual se llama this. De este modo el código casi completamente correcto de la operación es:

Código:
  1. void ModificarNombrePersona(String nombre){
  2. this.nombre= nombre;
  3. }


Entonces, Java nos provee SIEMPRE, queramos o no, una referencia al objeto con el que hemos llamado a una operación. Esta referencia se utiliza con la palabra reservada this. De este modo, si el llamado a la operación fue

Código:
  1. p1.ModificarNombrePersona(“Kyshuo”);


dentro de la operación, la referencia this tendrá el mismo valor que p1, es decir this y p1 serán alias. Espero que esto se entienda, porque es crucial en Java y, aunque de pronto ahora podemos prescindir del uso de this, luego no habrá manera de avanzar sin esta referencia, por tanto les pediré que pregunten todo lo que les haga falta.

Yo creo que una buena forma de adaptarse a esto es pensar que ya no necesitamos para nada declarar en nuestras operaciones un parámetro que sea del tipo del objeto que estamos tratando porque Java siempre lo recibirá de forma implícita y lo llamará this. Es como si en Modula la operación ModificarNombrePersona tuviera esta firma:

Código:
  1. PROCEDURE ModificarNombrePersona(nombre: TipoNombre; VAR this: Persona);


Java entonces SIEMPRE declara por nosotros un parámetro del tipo objeto en cuestión y lo llama this. Con eso nos ahorramos tener que pasar los objetos como argumentos de las operaciones y además no tenemos que declararlos más en los encabezados de las mismas.

No está de más aclarar que el objeto pasado a la operación se pasa por copia (por valor), por eso digo que this y p1 son alias. De este modo realmente es como si en Modula la firma fuera esta:

Código:
  1. PROCEDURE ModificarNombrePersona(nombre: TipoNombre; this: Persona);


-------------------------------------------------------------------------------------

Funciones en Java:

Como ya dije, las operaciones en Java se declaran siempre de la misma manera sin importar si son procedimientos o funciones:

Código:
  1. tipoDeRetorno Identificador (argumentos);


Ustedes ya tienen bien claro que un procedimiento no retorna nada y en cambio una función sí lo hace. De este modo, para declarar un procedimiento le damos el tipo de retorno void, que justamente es el tipo nulo de Java para retornos. Claramente entonces, si ponemos otro tipo de retorno distinto de void estamos declarando una función. Veamos, ahora que nuestra clase Persona tiene sus atributos como privados, cómo utilizar una función para que nos retorne el nombre. Recordémosla primero en Modula:

Código:
  1. PROCEDURE ObtenerNombrePersona(p: Persona): TipoNombre;
  2. BEGIN
  3. RETURN p^.Nombre;
  4. END ObtenerNombrePersona;


En Java será:

Código:
  1. String ObtenernombrePersona(){
  2. return this.nombre;
  3. }


Seguimos un poco con el tema de la referencia this. Verán que en Modula hacía falta pasar el objeto Persona como argumento pero, como ya dije, Java siempre lo pasa de forma implícita y le llama this, por tanto no necesitamos declarar ningún argumento para esta operación.
Noten que es una función que retorna algo del tipo String, por tanto podemos usarla a la izquierda de una asignación, cosa que no podemos con un procedimiento.

En Java, las funciones también tienen una sentencia return que sí o sí es requerida, la cual funciona exactamente igual que en Modula, es decir que, llegada a la sentencia return salimos de la operación sin importar si había código por ejecutarse debajo. Mucho ojo con eso.
Los procedimientos, o funciones nulas como los llaman algunos (para mí esa nomenclatura es errónea), no requieren sentencia return porque justamente no retornan nada. Sin embargo podemos poner si queremos una sentencia return vacía:

Código:
  1. return;


Si la usamos, NetBeans la subrayará en amarillo y nos dirá que es innecesaria, sin embargo compilará y funcionará correctamente si la dejamos ahí.

No colocar al menos una sentencia return en una función es un error de sintaxis y NetBeans nos lo indicará enseguida. Cuando una función se vuelve compleja y tiene muchos if y bucles tendemos a tener distintas sentencias return de modo que se realizará la una o la otra. NetBeans chequeará que las condiciones aseguren que siempre habrá alguna sentencia return que se ejecutará, si no es así nos dirá que hay un error de sintaxis. Por ejemplo:

Código:
  1. if (X>=0) then
  2. return “Mayor que cero”;
  3. else
  4. X= 0;


En ese caso si X es mayor o igual que 0 retornamos un String (suponiendo que ese código corresponde a una función de tipo String), pero si no es así le asignamos a X el valor 0 y ya. En ese caso NetBeans detecta que hay posibilidad de llegar a un momento en que no haya un return posible (cuando entramos en el else) y nos marcará un error. De este modo tenemos dos opciones:

  • Agregamos una sentencia return en el else.
  • Tenemos una variable declarada específicamente para guardar el valor a retornar y declaramos así una única sentencia return fuera del if.


La segunda opción es la más recomendable, cada uno lo hace como quiera. Veamos un ejemplo con el pequeño código de arriba:

Código:
  1. . . .
  2. String ret; //La variable para retornar algo.
  3.  
  4. if(X>=0)
  5. ret= “Mayor que 0;
  6. else{
  7. X=0;
  8. ret= “Asignado a 0;
  9. }
  10.  
  11. . . .
  12. return ret;


-------------------------------------------------------------------------------------

Operaciones públicas y privadas:

Las operaciones que hemos declarado en nuestra clase Persona han sido operaciones accesibles desde otras clases, en este caso, las accedíamos desde la clase principal. Esto implica que son operaciones públicas de la clase Persona. En Módula esto se especificaba declarando las operaciones en el DEF ya que lo que allí aparecía justamente era público y se podía usar en otros Módulos. En Java se hace añadiendo el modificador de acceso public antes de declarar la operación:

Código:
  1. public void ModificarNombrePersona(String nombre){
  2. nombre= nombre;
  3. }
  4.  
  5. public String ObtenernombrePersona(){
  6. return this.nombre;
  7. }


¿Por qué si nosotros no habíamos puesto el modificador public en estas operaciones podíamos acceder externamente a ellas de todos modos? Pues porque si no especificamos ningún modificador de acceso Java implícitamente añade el modificador public, de este modo da igual ponerlo o no ponerlo, pero es una convención de programadores SIEMPRE añadir el modificador public para las operaciones y atributos públicos.

Podemos declarar operaciones como private, es decir, operaciones que no serán accesibles desde fuera de la clase sino que serán privadas de ella. Esto es como si en Modula declaráramos una operación en el MOD que no estuviera en el DEF. Las operaciones privadas son declaradas cuando necesitamos hacer alguna tarea propia de la clase que no tiene por qué ser usada fuera y en efecto, no queremos que se pueda hacer.

Vamos a hacer entonces el TAD Persona que teníamos en Modula pero ahora en Java. Recordemos primero el archivo DEF de este TAD:

Código:
  1. (******************************************************************************
  2. Un objeto Persona estará representado por los siguientes datos:
  3.  
  4. DOCUMENTO
  5. NOMBRE
  6. APELLIDO
  7. EDAD
  8. SUELDO POR HORA
  9. HORAS TRABAJADAS
  10. Esta interfáz describe las operaciones que se pueden realizar con un objeto del
  11. tipo Persona.*)
  12.  
  13. DEFINITION MODULE Persona;
  14. CONST
  15. MAX_LARGO_NOMBRE= 50;
  16. TYPE
  17. Persona; (*Es un tipo opaco.*)
  18. (*Tipo para el nombre y el apellido*)
  19. TipoNombre= ARRAY[1..MAX_LARGO_NOMBRE] OF CHAR;
  20. (************************************)
  21. (* CONSTURCOTRAS *)
  22. (************************************)
  23.  
  24. (*Crea un nuevo objeto persona a partir de los datos ingresados como parámetros*)
  25. PROCEDURE CrearPersona(documento: INTEGER; nombre, apellido: TipoNombre;
  26. edad: INTEGER; sueldo, horas: REAL): Persona;
  27.  
  28. (************************************)
  29. (* SELECTORAS *)
  30. (************************************)
  31.  
  32. (*Retorna el documento de una persona pasada como parámetro*)
  33. PROCEDURE ObtenerDocumentoPersona(p: Persona): CARDINAL;
  34.  
  35. (*Retorna el nombre de una persona pasada como parámetro*)
  36. PROCEDURE ObtenerNombrePersona(p: Persona): TipoNombre;
  37.  
  38. (*Retorna el apellido de una persona pasada como parámetro*)
  39. PROCEDURE ObtenerApellidoPersona(p: Persona): TipoNombre;
  40.  
  41. (*Retorna la edad de una persona pasada como parámetro*)
  42. PROCEDURE ObtenerEdadPersona(p: Persona): CARDINAL;
  43.  
  44. (*Retorna el sueldo que una persona gana por hora trabajada*)
  45. PROCEDURE ObtenerSueldoPorHora(p: Persona): REAL;
  46.  
  47. (*Retorna la cantidad de horas trabajadas por una persona*)
  48. PROCEDURE ObtenerHorasTrabajadas(p: Persona): REAL;
  49.  
  50. (*Retorna el sueldo ganado por una persona según el sueldo que gana por
  51. hora trabajada y la cantidad de horas trabajadas*)
  52. PROCEDURE CalcularSueldoGanado(p: Persona): REAL;
  53.  
  54. (************************************)
  55. (* MODIFICADORAS *)
  56. (************************************)
  57.  
  58. (*Asigna un nuevo documento a la persona pasada como parámetro*)
  59. PROCEDURE ModificarDocumentoPersona(documento: CARDINAL; VAR p: Persona);
  60.  
  61. (*Asigna un nuevo nombre a la persona pasada como parámetro*)
  62. PROCEDURE ModificarNombrePersona(nombre: TipoNombre; VAR p: Persona);
  63.  
  64. (*Asigna un nuevo apellido a la persona pasada como parámetro*)
  65. PROCEDURE ModificarApellidoPersona(apellido: TipoNombre; VAR p: Persona);
  66.  
  67. (*Asigna una nueva edad a la persona pasada como parámetro*)
  68. PROCEDURE ModificarEdadPersona(edad: CARDINAL; VAR p: Persona);
  69.  
  70. (*Asigna un nuevo sueldo a ganar por hora trabajada para la persona
  71. pasada como parámetro*)
  72. PROCEDURE ModificarSueldoPorHora(sueldo: REAL; VAR p: Persona);
  73. (*Asigna una cantidad de horas trabajadas para la persona pasada ocmo parámetro*)
  74. PROCEDURE ModificarHorasTrabajadas(horas: REAL; VAR p: Persona);
  75.  
  76. (************************************)
  77. (* INFORMACIÓN *)
  78. (************************************)
  79.  
  80. (*Imprime los datos de p en el siguiente formato:
  81. Documento: ValorDelDocumento
  82. Nombre: NombrePersona
  83. Apellido: ApellidoPersona
  84. Edad: EdadPersona
  85. Sueldo por hora: ValorDelSueldo
  86. Horas trabajadas: CantidadDeHorasTrabajadas*)
  87. PROCEDURE ImprimirPersona(p: Persona);
  88.  
  89. (************************************)
  90. (* DESTRUCTORAS *)
  91. (************************************)
  92.  
  93. (*Liebera la memoria ocupada por p*)
  94. PROCEDURE DestruirPersona(VAR p: Persona);
  95. END Persona.


Como primera observación interesante hay que destacar que el constructor que teníamos en este TAD no lo hemos construido aún en Java y pues, no he hablado mucho de los constructores de este lenguaje, así que vayamos a ello.

-------------------------------------------------------------------------------------

Constructores en Java:

Hasta ahora solo sabemos que Java nos provee un constructor por defecto el cual posee el mismo nombre de la clase en cuestión y no recibe argumentos. Nosotros, sin tan siquiera declararlo en la clase podemos invocarlo para solicitar memoria y crear el nuevo objeto. El hecho es que este constructor inicializa todos los atributos con un valor por defecto lo cual ya expliqué. ¿Y si no quiero que los atributos se inicialicen por defecto sino que yo quiero darles un valor o hacer tareas en la construcción del objeto? ¿Y si quiero pasar argumentos al constructor para directamente construir un objeto con datos definidos u obtenidos de alguna parte?
El constructor que teníamos en Modula recibía todos los datos necesarios para dar valores a todos los atributos del TAD Persona, a esto se le conoce como constructor completo. Sin embargo podríamos haber definido algún constructor con menos argumentos, por ejemplo, solo con el nombre y el apellido, dejando lo demás a la deriva de una inicialización predefinida.

En este ejemplo concreto no tiene mucho sentido hacer tareas en el constructor por defecto, pero a medida que los sistemas a desarrollar se vuelven complejos la forma de inicializar un objeto al crearlo se complicará. Java nos permite entonces:

  • Crear nuestro propio constructor por defecto (en realidad es sobreescribir el constructor por defecto de Java, pero lo veremos cuando lleguemos a la Herencia).
  • Crear tantos constructores como necesitemos.


Para inicializar los atributos de un objeto con un valor por defecto a nuestro gusto tenemos dos opciones:

  • Asignamos dichos valores ya al definir los atributos.
  • Asignamos dichos valores dentro del constructor.


La primera forma sería como muestro a continuación, suponiendo que el código fuente de la clase Persona es este:

Código:
  1. public class Persona {
  2. private String nombre= "ninguno";
  3. private String apellido= "ninguno";
  4.  
  5. public void ModificarNombrePersona(String nombre){
  6. this.nombre= nombre;
  7. }
  8.  
  9. public String ObtenernombrePersona(){
  10. return this.nombre;
  11. }
  12. }


Como ven, los atributos están inicializados ya en la declaración. Podemos también declarar eso mismo en una sola línea. Lo he hecho por separado para que todo se visualice lo mejor posible.

Ahora escribamos el constructor por defecto y demos los valores allí:

Código:
  1. public class Persona {
  2. private String nombre;
  3. private String apellido;
  4.  
  5. public Persona(){
  6. this.nombre= "ninguno";
  7. this.apellido= "ninguno";
  8. }
  9.  
  10. public void ModificarNombrePersona(String nombre){
  11. this.nombre= nombre;
  12. }
  13.  
  14. public String ObtenernombrePersona(){
  15. return this.nombre;
  16. }
  17. }


Agregamos a la clase entonces una nueva operación. Esta operación es extraña por dos cosas:

  • Tiene el mismo nombre de la clase.
  • No tiene ningún tipo de retorno, ni siquiera void.


Los constructores de Java tienen reglas bien específicas las cuales son justamente las de arriba:

[*]Deben llevar el mismo nombre de la clase a la que pertenecen.
•[*]No deben tener ningún tipo de retorno asociado, ni siquiera void.

Un constructor en Java, que no recibe argumento ninguno, es justamente el constructor por defecto. Si nosotros declaramos, como hice ahora, un constructor sin argumentos nos estamos definiendo nuestro propio constructor por defecto. Cuando desde fuera se hace, por ejemplo:

Código:
  1. p1= new Persona();


estamos justamente llamando a una operación. La diferencia entre los llamados habituales y uno de construcción de un objeto es que estos últimos se hacen mediante el operador new y a la izquierda de una asignación a una referencia. Así que como ven, luego de new va el constructor que queremos invocar.

El tipo Persona en Modula era, como ya habíamos recordado anteriormente:

Código:
  1. TYPE
  2. Persona= POINTER TO DatosPersona;
  3.  
  4. DatosPersona= RECORD
  5. Nombre, Apellido: TipoNombre;
  6. Edad, Documento: CARDINAL;
  7. SueldoPorHora: REAL;
  8. HorasTrabajadas: REAL;
  9. END;



Declaremos entonces el resto de los atributos en nuestro TAD Persona de Java, tras lo cual completaremos el código del constructor por defecto para inicializar todos los valores de manera correcta y declararemos además un constructor completo. Hasta ahora la clase Persona es esta:

Código:
  1. public class Persona {
  2. private String nombre, apellido;
  3. private int edad, documento;
  4. double sueldoPorHora, horasTrabajadas;
  5.  
  6. public Persona(){
  7. this.nombre= "ninguno";
  8. this.apellido= "ninguno";
  9. }
  10.  
  11. public Persona(String nombre, String apellido, int edad, int documento,
  12. double sueldoPorHora, double horasTrabajadas){
  13. this.nombre= nombre;
  14. this.apellido= apellido;
  15. this.edad= edad;
  16. this.documento= documento;
  17. this.sueldoPorHora= sueldoPorHora;
  18. this.horasTrabajadas= horasTrabajadas;
  19. }
  20.  
  21. public void ModificarNombrePersona(String nombre){
  22. this.nombre= nombre;
  23. }
  24.  
  25. public String ObtenernombrePersona(){
  26. return this.nombre;
  27. }
  28. }


Bien, tenemos entonces dos constructores, es decir, dos operaciones constructoras. Algo que tal vez a ustedes les llame mucho la atención es que tenemos dos operaciones con el mismo nombre. Esto es algo nuevo y se conoce como Polimorfismo, es decir, una misma operación puede tener muchas formas. En este caso tenemos el constructor Persona con dos formas diferentes. ¿Cómo sabrá Java a cual constructor estamos llamando? Pues por la cantidad y el tipo de argumentos. Veamos un ejemplo:

Código:
  1. p1= new Persona();
  2. p2= new Persona(“Kyshuo”,”Ayame”,23,12345678,80,30);


¿Está claro que para p1 hemos invocado al constructor por defecto y para p2 al constructor completo? Java se da cuenta de cual constructor debe llamar justamente porque uno no recibe argumentos y el otro sí. Asimismo podemos tener muchos constructores distintos siempre que el tipo y la cantidad de argumentos no coincidan a la vez en más de una operación. Por ejemplo:

Código:
  1. public Persona(String nombre, int documento);
  2.  
  3. public Persona(String apellido, int edad);


Claramente allí tenemos dos declaraciones distintas, sin embargo ambas tienen dos argumentos cuyos tipos coinciden. Al momento de la invocación Java no puede saber a qué constructor de ambos estamos queriendo llamar y pues por este motivo no nos dejará hacer estas declaraciones. Imaginen esto:

Código:
  1. p3= new Persona(“Ermenegildo”, 52);


La referencia p3 pretende ser inicializada mediante el constructor que recibe el nombre y la edad, sin embargo para Java es un constructor que recibe un String y luego un int y no puede diferenciar a cuál queremos invocar.

Si aún así quisiéramos tener esos dos constructores tendríamos que variar en algo, por ejemplo el orden de los argumentos:

Código:
  1. public Persona(int documento, String nombre);
  2.  
  3. public Persona(String apellido, int edad);


Ahora sí Java puede diferenciar una operación de otra porque la primera recibe un int y luego un String y la segunda recibe un String y luego un int.

Esto es aplicable a todas las operaciones, pero lo iremos viendo luego.


NOTA: Espero que hayan podido observar que para los argumentos de las operaciones siempre debemos declarar primero el tipo y luego el identificador, separando todos los argumentos con una coma. No es como sucedía en Pascal y Modula donde podíamos declarar varios argumentos de un mismo tipo a la vez separando por coma y luego usando el punto y coma para declarar argumentos de un nuevo tipo. Ahora en Java ponemos el tipo sí o sí por cada argumento.

¿Qué pasa si yo quiero que siempre se use SOLAMENTE el constructor completo para inicializar los objetos Persona?

Muchas veces necesitamos asegurarnos de que no se usará el constructor por defecto para crear algún objeto de una clase que estamos desarrollando ya que necesitamos más información explícitamente para lograr un buen funcionamiento. Una forma bien fácil de lograr esto es declarar el constructor por defecto como privado, así:

Código:
  1. private Persona(){}


Como ven, he abierto y cerrado las llaves {}, es decir, ni siquiera puse código fuente en el cuerpo del constructor porque no lo usaremos. Podría haberlo hecho si pretendiera usarlo internamente, pero para la clase Persona podríamos querer que siempre se inicialice con el constructor completo a fin de garantizarnos que siempre se creará un nuevo objeto con datos sustanciales. De este modo ya tenemos esa garantía, los clientes de mi clase no tendrán más que usar el constructor completo.

------------------------------------------------------------------------------------

Ejercicio:

Dado el TAD Persona que usamos en Modula 2 tal como lo he mostrado anteriormente, reescríbanlo en Java exceptuando la operación DestruirPersona. Esto último viene a que la destrucción de objetos en Java es muy distinta a la que conocen (lo veremos luego).

Modifiquen el método main de modo que pida al usuario un entero para la cantidad de personas a almacenar y cree con ese valor un arreglo de personas llamado nominaPersonas. Luego deberá pedir para cada persona los datos ingresados por el usuario e ir guardando dichas personas en el arreglo. Al final debe imprimir los datos de todas las personas cargadas en el formato que mostraré. Veamos un ejemplo de ejecución donde pinto en azul la entrada ingresada por el usuario:

Cita:
Ingrese la cantidad de personas a almacenar: 3

Nombre: Mengano
Apellido: Menganoso
Edad: 20
Documento: 123456
Sueldo: 15.5
Horas: 30

Nombre: Fulano
Apellido: Fulanoso
Edad: 20
Documento: 456789
Sueldo: 15.5
Horas: 30

Nombre: Perengano
Apellido: Perenganoso
Edad: 18
Documento: 258963
Sueldo: 12.3
Horas: 10

Los datos ingresados han sido:

Mengano Menganoso, 20 años, documento 123456, sueldo $15.5 la hora, horas trabajadas 30.
Fulano Fulanoso, 20 años, documento 456789, sueldo $15.5 la hora, horas trabajadas 30.
Perengano Perenganoso, 18 años, documento 258963, sueldo $12.3, horas trabajadas 10.


Este ejercicio será una gran práctica para ustedes. Por favor, háganlo.

------------------------------------------------------------------------------------

Nos veremos en la siguiente lección.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
pirdy
Usuario Inquieto


Registrado: 11 Feb 2010
Mensajes: 137

Mensaje Publicado: Viernes 17 Ago 2012 23:54

Título del mensaje: Re: Lección 67: Operaciones en Java - Introducción a la POO.

Responder citando

Muchas gracias por la dedicacion. Saludos profe.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Sebastián Marcos
Usuario Inquieto


Registrado: 04 Oct 2011
Mensajes: 133

Mensaje Publicado: Miércoles 22 Ago 2012 07:03

Título del mensaje: Re: Lección 67: Operaciones en Java - Introducción a la POO.

Responder citando

Excelente lección como siempre.
Continuamos al firme con el curso.
Saludos.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Konguito
Usuario Iniciado


Registrado: 04 Abr 2013
Mensajes: 14

Mensaje Publicado: Viernes 05 Abr 2013 21:42

Título del mensaje: Re: Lección 67: Operaciones en Java - Introducción a la POO.

Responder citando

Estoy haciendo el ejercicio de este tema (Que me ha resultado más difícil de lo que esperaba y me encuentro con un problema.
Cuando ingreso un double (15.5) en sueldo u horas me salta el error
Exception in thread "main" java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:909)
at java.util.Scanner.next(Scanner.java:1530)
at java.util.Scanner.nextDouble(Scanner.java:2456)
at datospersonas.DatosPersonas.main(DatosPersonas.java:40)
Java Result: 1
Y si pongo un número sin . funciona bien...
¿Qué ocurre?
Código:
package datospersonas;

import java.util.Scanner;


public class DatosPersonas {

   
    public static void main(String[] args) {
       
       
         
        int contador=0, pos=0;
        Scanner teclado = new Scanner(System.in);
        String nom,apell;
        int ed,doc;
        double suel,hor;
        Persona [] p ;
       
        System.out.println("Ingrese la cantidad de personas a almacenar:");
        contador = teclado.nextInt();
        teclado.nextLine();
        p= new Persona[contador];
        while (contador>0){
           
            System.out.print("Nombre: ");
            nom=teclado.nextLine();
            System.out.print("Apellido: ");
            apell=teclado.nextLine();
            System.out.print("Edad: ");
             ed=teclado.nextInt();
            System.out.print("Documento: ");
             doc=teclado.nextInt();
            System.out.print("Sueldo: ");
             suel=teclado.nextDouble();
            System.out.print("Horas: ");
             hor=teclado.nextDouble();
             teclado.nextLine();
             Persona per = new Persona(nom,apell,ed,doc,suel,hor);
             p[pos]= per;
             contador--;
             pos++;
             
        }
        System.out.println("Los datos ingresados han sido: ");
       
        for (int i = 0; i < p.length; i++) {
            System.out.println(p[i].ObtenernombrePersona().concat(" ").concat(p[i].getApellido())+", "+p[i].getEdad()+"años, documento "+p[i].getDocumento()+", sueldo $"+p[i].getSueldoPorHora()+" por hora, horas trabajadas "+p[i].getHorasTrabajadas()+".");
           
        }
    }

}
[/code]

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Kyshuo Ayame
Moderador Global


Registrado: 07 Ene 2011
Mensajes: 1044

Mensaje Publicado: Sábado 06 Abr 2013 01:02

Título del mensaje: Re: Lección 67: Operaciones en Java - Introducción a la POO.

Responder citando

Bien, es un detalle que no mencioné. Pues dentro del código fuente la coma es el punto, por ejemlpo:

Código:
  1. double numero= 12.5;


Sin embargo en la consola la coma es justamente la coma:

Cita:
Ingrese un numero: 12,5


Esto puede variar según la distribución de idioma del sistema operativo. Para el español pues es así.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Konguito
Usuario Iniciado


Registrado: 04 Abr 2013
Mensajes: 14

Mensaje Publicado: Sábado 06 Abr 2013 08:00

Título del mensaje: Re: Lección 67: Operaciones en Java - Introducción a la POO.

Responder citando

Ok. Gracias!

Volver arriba
Ver perfil del usuario Enviar mensaje privado
ale91
Usuario Iniciado


Registrado: 13 Abr 2012
Mensajes: 18

Mensaje Publicado: Lunes 05 Ago 2013 18:54

Título del mensaje: Re: Lección 67: Operaciones en Java - Introducción a la POO.

Responder citando

mmm no me quedo claro el concepto que diste de Polimorfismo pues una operación polimorfica se supone que su firma es exactamente igual, pero sus métodos son distintos.

En realidad No alcanza con que tengan el mismo nombre, para ser
polimorfismo deben ser realmente la misma operación.

Por eso yo diría que el constructor de Persona no es polimorfica.

Tipicamente las operaciones polimorficas se usan cuando hay una jerarquía de generalización donde cada clase derivada tiene un método asociaciado a la misma operación. (Osea se asocian diferentes métodos a una misma operación)

Sin embargo vos en una clase podes tener distintos constructores, ya sean por referencia, copia o por defecto, en este caso no son exactamente la misma operación ni hay otras clases asociadas con la misma operación, por lo tanto (en mi opinión) no es polimorfica.

Saludos

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Kyshuo Ayame
Moderador Global


Registrado: 07 Ene 2011
Mensajes: 1044

Mensaje Publicado: Lunes 05 Ago 2013 22:20

Título del mensaje: Re: Lección 67: Operaciones en Java - Introducción a la POO.

Responder citando

Lo que señalas es muy interesenta y muy correcto. Lo que yo aquí señalé como POLIMORFISMO es en realidad SOBRECARGA. Sin embargo lo aclaro más adelante cuando entro de lleno en el tema de polimorfismo y las formas de obtenerlo. Está claro que una operación con igual nombre que otra pero distinta firma no es la misma operación.

Entonces dirás ¿Por qué no lo aclaras desde el principio? Y pues porque en principio en mucha bibliografía está tratado erróneamente y como los lectores se supone recién comienzan no quise hacer un quiebre tan grande. Por eso espero a que afiansen conceptos para tratar correctamente el polimorfismo al hablar específicamente de él.

Un saludo.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
sr floyd
Usuario Activo


Registrado: 05 Sep 2011
Mensajes: 196

Mensaje Publicado: Miércoles 25 Dic 2013 02:16

Título del mensaje: Re: Lección 67: Operaciones en Java - Introducción a la POO.

Responder citando

Hola Kyshuo, estupenda lección!!
He comenzado con java y voy adaptándome a su sintaxis y operaciones; Por cierto, de esta lección tengo las siguientes dudas:
Dado el siguiente código:
Código:
1.public class DatosPersonas {

2.    public static void main(String[] args){

3.        Persona p= new Persona();

4.

5.        p.nombre= "Kyshuo";

6.        p.apellido= "Ayame";

7.

8.        System.out.println("Nombre: " + p.nombre + "\nApellido: " + p.apellido);
9.    }

10.}


Entiendo que las variables nombre y apellido de la clase Persona, así como la misma clase persona fueron hasta el momento declaradas como públicas con el modificador public, pero ¿cómo es posible acceder a ellas desde main(refiriéndome al código arriba) sin una declaración de importación? algo análogo a lo que se hacía en MODULA2
como
Código:
FROM Persona IMPORT nombre,apellido,Persona


La otra duda es con respecto al parametro implicito llamado this, tu has dicho: el objeto pasado a la operación se pasa por copia (por valor), por eso digo que this y p1 son alias.
entonces, c`omo se garantiza que el objeto en cuestion quede modificado por fuera del procedimiento una vezfinalice este?
Quizás se me esté escapando algo, tan típico en las transiciones de un lenguaje a otro jeje
Gracias, estupendas lecciones Kyshuo!

Volver arriba
Ver perfil del usuario Enviar mensaje privado MSN Messenger
Alfonso Lara
Usuario Iniciado


Registrado: 07 May 2019
Mensajes: 13

Mensaje Publicado: Jueves 09 May 2019 08:30

Título del mensaje: Lección 67: Operaciones en Java - Introducción a la POO.

Responder citando

Hola, ¿el tutor está disponible en este curso aún? veo fechas del 2011. Muchas gracias.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Kyshuo Ayame
Moderador Global


Registrado: 07 Ene 2011
Mensajes: 1044

Mensaje Publicado: Viernes 17 May 2019 20:26

Título del mensaje: Lección 67: Operaciones en Java - Introducción a la POO.

Responder citando

Alfonso Lara escribió:
Hola, ¿el tutor está disponible en este curso aún? veo fechas del 2011. Muchas gracias.


Hola Alfonso, sí, estoy disponible. A veces me tardo en responder, pero siempre ando vigilando los comentarios.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Responder al Tema
Mostrar mensajes anteriores:   
Ir a:  
Todas las horas están en GMT + 2 Horas

Temas relacionados

Tema Autor Foros Respuestas Publicado
El foro no contiene ningún mensaje nuevo

¿Cómo instalar correctamente JDK (Java Develope...

Alfonso Lara Temas generales 2 Martes 07 May 2019 22:09 Ver último mensaje
El foro no contiene ningún mensaje nuevo

Ayuda con TestNG Parametrico en java netbeans

baltigo Java 0 Sábado 30 Jun 2018 01:37 Ver último mensaje
El foro no contiene ningún mensaje nuevo

Publicar Aplicación web JAVA en tomcat

Irvin Java Web 1 Viernes 13 Oct 2017 17:54 Ver último mensaje
El foro no contiene ningún mensaje nuevo

Imagen referencial De creación de jtable en apl...

Jensel S.G Java 6 Miércoles 13 Sep 2017 20:06 Ver último mensaje
El tema está bloqueado: no pueden editarse ni agregar mensajes.

EMPLEO ANALISTA PROGRAMADOR/A BACK-END JAVA EE

GRUPOARESTORA Bolsa de trabajo 0 Viernes 23 Jun 2017 14:33 Ver último mensaje
Panel de Control
No puede crear mensajes, No puede responder temas, No puede editar sus mensajes, No puede borrar sus mensajes, No puede votar en encuestas,