Download Unidad 2.- Mensajes y métodos 2.1 Atributos Const (final en java) Y

Document related concepts
no text concepts found
Transcript
Unidad 2.- Mensajes y métodos
2.1 Atributos Const (final en java) Y Static
CONST EN LAS CLASES
En una aplicación posiblemente nos encontremos con algún valor que permanece constante
durante la ejecución. Podemos definirla como una variable común pero perderíamos el
control. Por allí, en algún descuido, se cambiaría de valor pero no nos enteraríamos.
Podemos agregar a la definición de variable el modificador final. La sintaxis es la siguiente:
final tipo_variable nombre de variable [= valor];
Por ejemplo:
final int unaConstante = 10;
Si tratamos de modificar el valor de esta constante, el compilador indicará un error. Una vez
definido el valor no se puede modificar.
En java la palabra reservada final es utilizada en diversos contextos para definir una entidad
que no podrá ser modificada posteriormente.
 Clases final.
 Métodos final.
 Variables final.
Clases final
Una clase con el atributo final no puede heredar. Esto puede utilizarse por razones de
seguridad y eficiencia. Muchas de las clases de la librería estándar de java contienen el
atributo final, como por ejemplo java.lang.System y java.lang.String. Todos los métodos en
una clase final son implisitamente final también.
Ejemplo:
public final class MyFinalClass {...}
Métodos final.
Un método con el atributo final no puede redefinirse (override) en una subclase. Esto se usa
para prevenir que el método sea alterado en una subclase si el funcionamiento de este es
crucial en el funcionamiento o consistencia de la clase.
Ejemplo.
Public class MiClase
{
Public final void miMetodoFinal()
{
--}
}
Variables final.
Una variable final sólo se puede asignar una vez. Esta asignación no le otorga el estado
inmutable a la variable. Si la variable es un campo de una clase, debe asignarse en el
constructor de su clase. (Nota: si la variable es una referencia, esto significa que la variable
no puede ser re-bound para hacer referencia a otro objeto. Pero el objeto que hace
referencia es todavía mutable, si era originalmente mutable).
A diferencia del valor de una constante, el valor de una variable final no necesariamente se
conoce en tiempo de compilación.
Ejemplo.
public class Sphere
{
public static final
public final double
public final double
public final double
public final double
double PI= 3.141592653589793; //Esto es esencialmente una constante
radius;
xpos;
ypos;
zpos;
Sphere(double x, double y, double z, double r)
{
radius = r;
xpos = x;
ypos = y;
zpos = z;
}
[...]
}
Cualquier intento para reasignar un valor a estas variables producira un error de compilación.
Para ilustrar que finalmente esto no garantiza inmutabilidad: supongamos que reemplace las
variables de tres posiciones con una sola:
public final Position pos;
Donde pos es un objeto con tres propiedades pos.x, pos.y y pos.z. Entonces, no se puede
reasignar pos, pero las tres propiedades si, a menos que estas sean explícitamente finales
también.
El uso de una constante dentro de una clase significa “Esto es constante durante la vida del
objeto”. Por otra parte, en cada objeto la constante puede contener un valor diferente. Por
eso, cuando crea una constante ordinaria (no estática) dentro de una clase, no puede darle
un valor inicial. Esta inicialización debe ocurrir en el constructor. Como la constante se debe
inicializar en el punto en que se crea, en el cuerpo del constructor la constante debe estar ya
inicializada. De otro modo, le quedaría la opción de esperar hasta algún punto posterior en el
constructor, lo que significaría que la constante no tendría valor por un momento. Y nada
impediría cambiar el valor de la constante en varios sitios del constructor.
ATRIBUTO STATIC
A pesar de lo que podría parecer por su nombre, heredado de la terminología de C++, el
modificador static no sirve para crear constantes, sino para crear miembros que pertenecen a
la clase, y no a una instancia de la clase. Esto implica, entre otras cosas, que no es
necesario crear un objeto de la clase para poder acceder a estos atributos y métodos. Este
es el motivo por el cual es obligatorio que main se declare como static; de esta forma no
tenemos que ofrecer un constructor vacío para la clase que contiene el método, o indicar de
alguna forma a la máquina virtual cómo instanciar la clase.
Un uso del modificador static sería, por ejemplo, crear un contador de los objetos de la clase
que se han creado, incrementando la variable estática en el constructor:
class Usuario
{
static int usuarios = 0;
Usuario()
{
usuarios++;
}
}
Como es de esperar, dado que tenemos acceso a los atributos sin necesidad de crear un
objeto, los atributos estáticos como usuarios no se inicializan al crear el objeto, sino al cargar
la clase.
Podemos acceder a estos métodos y atributos bien desde la propia clase
public class Ejemplo
{
public static void main(String[] args)
{
Usuario raul = new Usuario();
Usuario juan = new Usuario();
System.out.println("Hay " + Usuario.usuarios + " usuarios");
}
}
o bien desde una instancia cualquiera de la clase:
public class Ejemplo
{
public static void main(String[] args)
{
Usuario raul = new Usuario();
Usuario juan = new Usuario();
System.out.println("Hay " + raul.usuarios + " usuarios");
}
}
Otro uso sería el de crear una recopilación de métodos y atributos relacionados a los que
poder acceder sin necesidad de crear un objeto asociado, que podría no tener sentido o no
ser conveniente, como es el caso de la clase Math.
public class Ejemplo
{
public static void main(String[] args)
{
System.out.println("PI es " + Math.PI);
System.out.println("El coseno de 120 es " + Math.cos(120));
}
}
Una característica no muy conocida que se introdujo en Java 1.5 son los static imports, una
sentencia similar al import habitual, con la salvedad de que esta importa miembros estáticos
de las clases, en lugar de clases de los paquetes, permitiendo utilizar estos miembros sin
indicar el espacio de nombres en el que se encuentran. El ejemplo anterior podría haberse
escrito también de la siguiente forma utilizando esta característica:
import static java.lang.Math.*;
public class Ejemplo
{
public static void main(String[] args)
{
System.out.println("PI es " + Math.PI);
System.out.println("El coseno de 120 es " + Math.cos(120));
}
}
Si por algún motivo requerimos cualquier tipo de computación para inicializar nuestras
variables estáticas, utilizaremos lo que se conoce como bloque estático o inicializador
estático, el cual se ejecuta una sola vez, cuando se carga la clase.
public class Reunion
{
static
{
int zona_horaria = Calendar.getInstance().get(Calendar.ZONE_OFFSET)/(60 * 60 * 1000);
}
}
Por último, una curiosidad relacionada que podéis utilizar para romper el hielo con una
programadora Java es que podemos utilizar un bloque static para escribir un programa
sencillo sin necesidad de un main, añadiendo una llamada a System.exit para que el
programa termine tras cargar la clase sin intentar llamar al método main.
public class Ejemplo
{
static
{
System.out.println("Hola mundo");
System.exit(0);
}
}
2.2 Concepto de Método
En Java toda la lógica de programación (Algoritmos) está agrupada en funciones o métodos.
Un método es:





un subprograma
Un bloque de código que tiene un nombre
recibe unos parámetros o argumentos (opcionalmente)
contiene sentencias o instrucciones para realizar algo (opcionalmente)
devuelve un valor de algún Tipo conocido (opcionalmente).
Los métodos son las acciones funciones o procedimientos que realiza nuestro programa; los
métodos son subrutinas que manipulan los datos definidos por una clase.
CARACTERISTICAS DE LOS METODOS:
1. Contiene una o más declaraciones
2. Cada método tiene un nombre y este nombre se utiliza para llamar al método (las
palabras clave no pueden ser utilizadas como el nombre del método).
3. Debe llevar paréntesis después del nombre.
4. El método main() está reservado por java como el método que inicializa la
ejecución del programa.
2.3 Declaración de Métodos
La sintaxis global es:
Tipo_Valor_devuelto nombre_método ( lista_argumentos )
{
bloque_de_codigo;
}
y la lista de argumentos se expresa declarando el tipo y nombre de los mismos (como en las
declaraciones de variables). Si hay más de uno se separan por comas.
Por ejemplo:
int sumaEnteros ( int a, int b )
{
int c = a + b;
return c;
}
•El método se llama sumaEnteros.
•Recibe dos parámetros también enteros. Sus nombres son a y b.
•Devuelve un entero.
En el ejemplo la claúsula return se usa para finalizar el método devolviendo el valor de la
variable c.
2.4 Llamadas Métodos Mensajes
El modelado de objetos no sólo tiene en consideración los objetos de un sistema, sino
también sus interrelaciones.
Mensaje.
Los objetos interactúan enviándose mensajes unos a otros. Tras la recepción de un mensaje
el objeto actuará. La acción puede ser el envío de otros mensajes, el cambio de su estado, o
la ejecución de cualquier otra tarea que se requiera que haga el objeto.
Método.
Un método se implementa en una clase, y determina cómo tiene que actuar el objeto cuando
recibe un mensaje. Cuando un objeto A necesita que el objeto B ejecute alguno de sus
métodos, el objeto A le manda un mensaje al objeto B.
Al recibir el mensaje del objeto A, el objeto B ejecutará el método adecuado para el mensaje
recibido.
Llamar a Métodos de un Objeto.
Llamar a un método de un objeto es similar a obtener una variable del objeto. Para llamar a
un método del objeto, simplemente se añade al nombre del objeto referenciado el nombre del
método, separados por un punto (‘.’), y se proporcionan los argumentos del método entre
paréntesis. Si el método no necesita argumentos, se utilizan los paréntesis vacios.
objetoReferenciado.nombreMétodo(listaArgumentos);
o
objetoReferenciado.nombreMétodo();
Las llamadas a métodos se hacen directamente a un objeto específico; el objeto especificado
en la llamada al método es el que responde a la instrucción.
Las llamadas a métodos también se conocen como mensajes.
Como en la vida real, los mensajes se deben dirigir a un receptor particular.
Se pueden obtener distintos resultados dependiendo del receptor de su mensaje.
Una llamada a un método es una expresión y evalúa a algún valor. El valor de una llamada a
un método es su valor de retorno, si tiene alguno.
Normalmente se asignará el valor de retorno de un método a una variable o se utilizará la
llamada al método dentro del ámbito de otra expresión o sentencia.
Recuerda que una llamada a un método es un mensaje al objeto nombrado.
Como se explicó anteriormente, el objetoReferenciado en la llamada al método
objetoReferenciado.metodo() debe ser una referencia a un objeto. Como se puede utilizar un
nombre de variable aquí, también se puede utilizar en cualquier expresión que devuelva una
referencia a un objeto. Recuerda que el operador new devuelve una referencia a un objeto.
Por eso, se puede utilizar el valor devuelto por new para acceder a las variables del nuevo
objeto.
new Rectangle(0, 0, 100, 50).equals(anotherRect) La expresión new Rectangle(0, 0, 100, 50)
evalúa a una referencia a un objeto que se refiere a un objeto Rectangle.
Entonces, como verás, se puede utilizar la notación de punto (‘.’) para llamar al método
equals() del nuevo objeto Rectangle para determinar si el rectangúlo nuevo es igual al
especificado en la lista de argumentos de equals().
2.6 Referencia This
Los métodos de un objeto, como se explicó anteriormente, definen las acciones que un
objeto puede llevar a acabo. Para utilizar los métodos de un objeto, usamos la misma técnica
que para asignar colores a los datos de un objeto:
Circulo c = new Circulo();
double a;
c.r = 2.5;
a = c.area();
En este último ejemplo se asignó el valor del método area( ) a la variable a. Observe la última
línea. No se escribió a = area(); sino: a = c.area();
Esto se debe a que se utilizó la programación “Orientada a Objetos”; aquí el objeto es el
centro de atención y no así la llamada del método. Esto es probablemente la más simple e
importante característica del paradigma orientado a objetos.
Note que no se pasó ningún argumento a c.area(). El objeto sobre el que se está operando,
c, se encuentra implícito en la sintaxis. Observe de nuevo el ejemplo 1: notará lo mismo en la
definición del método area() - no toma argumentos. Esta implícito en el lenguaje, que un
método opera sobre una instancia de la clase de la cual es definido. De este modo el método
area() puede utilizar libremente el campo r de la clase - es claro que esta refiriéndose al radio
de cualquier instancia Circulo que invoca el método. ¿Qué es lo que pasa aquí? ¿Cómo
puede saber un método, que no toma ningún parámetro, sobre que dato operar? En efecto,
el método area() simula tener un argumento, y se define con un argumento implícito que no
se muestra en la declaración del mismo. El argumento implícito es llamado this, y se refiere a
“este objeto” - el objeto Circulo del cual el método se invoco. This. es frecuentemente
llamado la referencia “this”.
El argumento implícito this, no se muestra en la declaración del método, ya que usualmente
no es necesario - donde quiera un método en Java puede accesar los campos en su clase. El
código de un método constructor de la clase Circulo se muestra en el siguiente ejemplo:
public Circulo(double a, double b, double c)
{
x = a;
y = b;
r = c;
}
En este caso, el método constructor inicializa los campos de datos del objeto que se está
creando. Este método es equivalente al que se muestra en este otro ejemplo:
public Circulo(double a, double b, double c)
{
this.x = a;
this.y = b;
this.r = c;
}
Cada vez que este último constructor se ejecuta, this hace referencia al objeto que se está
creando en ese instante.
La palabra this se puede usar explícitamente cuando se quiere poner en claro que un método
accesa sus propias variables y/o métodos. Por ejemplo, se puede re-escribir el método area()
de la siguiente forma:
public double area()
{
return 3.14159 * this.r * this.r;
}
En un método tan sencillo como éste, no es necesario ser explícito. Sin embargo, en casos
más complicados, parece que utilizar un this explícito incrementa la claridad del código
aunque no sea estrictamente necesario.
2.7 Forma de pasar argumentos
Algunos métodos requieren que se les pasen argumentos (parámetros). Los tipos de los
parámetros deberán especificarse en la declaración de cada método. En general, existen dos
formas de pasar parámetros:


Por valor y
Por referencia.
Paso por valor. Todas las variables que aparezcan en una lista de parámetros serán
consideradas parámetros por valor, a menos que la lista contenga la palabra clave out o la
palabra clave ref. En el paso por valor se envía una copia del valor del parámetro, de manera
que el método que recibe ese valor no puede cambiar el contenido de la variable utilizada en
el envío del mensaje.
Dado que java no soporta el paso por referencia pondré el ejemplo en C#.
Ejemplo: // pasoPorValor.cs : Ejemplifica el paso de parámetros por valor.
using System;
using C = System.Console;
class Receptor
{
int s ;
public Receptor( int x )
{
x+=10 ;
C.Write Line?(“ El valor de x es : {0} “, x ) ;
s = x ;
}
}
class Principal
{
public static void Main( )
{
int a = 20 ;
Receptor r = new Receptor ( a ) ;
C.Write Line(“ El valor de a es : {0}”, a ) ;
}
}
Paso por referencia. En el paso de parámetros por referencia, en lugar de pasar una copia
del valor almacenado en la variable, se pasa la dirección de memoria de ella.
Así, el método receptor puede modificar el contenido de la variable.
En C#, los parámetros por referencia se crean utilizando la palabra clave ref en la lista de
parámetros del método.
Ejemplo: // pasoPorRef.cs : Ejemplifica el paso de parámetros por referencia.
using System;
using C = System.Console;
class Receptor
{
int s ;
public Receptor( ref int x )
{
x+=10 ;
C.Write Line(“ El valor de x es : {0} “, x ) ;
s = x ;
}
}
class Principal
{
public static void Main( )
{
int a = 20 ;
Receptor r = new Receptor ( ref a ) ;
C.Write Line(“ El valor de a es : {0} “, a ) ;
}
}
2.8 Devolver un valor desde un método
Devolver un Valor desde un Método
Java necesita que un método declare el tipo de dato del valor que devuelve. Si un método no
devuelve ningún valor, debe ser declarado para devolver void (nulo).
Los métodos pueden devolver tipos de datos primitivos o tipos de datos de referencia. El
método estaVacio() de la clase Pila devuelve un tipo de dato primitivo, un valor booleano.
class Pila
{
static final int PILA_VACIA = −1;
Object[] stackelements;
int topelement = PILA_VACIA;
. . .
boolean estaVacio()
{
if (topelement == PILA_VACIA)
return true;
else
return false;
}
}
Sin embargo, el método pop de la clase PILA devuelve un tipo de dato de referencia: un
objeto.
class Pila
{
static final int PILA_VACIA = −1;
Object[] stackelements;
int topelement = PILA_VACIA;
. . .
Object pop()
{
if (topelement == PILA_VACIA)
return null;
else
{
return stackelements[topelement—];
}
}
}
Los métodos utilizan el operador return para devolver un valor. Todo método que no sea
declarado como void debe contener una sentencia return.
El tipo de dato del valor devuelto por la sentencia return debe corresponder con el tipo de
dato que el método tiene que devolver; no se puede devolver un objeto desde un método que
fue declarado para devolver un entero.
Cuando se devuelva un objeto, el tipo de dato del objeto devuelto debe ser una subclase o la
clase exacta indicada. Cuando se devuelva un tipo interface, el objeto retornado debe
implementar el interface especificado.
2.9 Estructura del código
Estructura básica de un programa en Java
En Java, como en cualquier otro lenguaje orientado a objetos, abandonamos el modo de
entender un programa que utilizábamos anteriormente para aproximarnos a un modo más
cercano a “la vida misma”.
Los programas ahora estarán divididos en clases. Una clase en si se puede entender como
un programa independiente, tiene sus propios datos y también maneja esos datos “a su
modo”.
La relación con la vida misma la podemos ver en el siguiente comentario: Imaginemos que
dos clases tal y como las hemos explicado anteriormente son “la clase mecánico” y la “clase
panadero”. Cada una de estas clases tiene sus propias herramientas y sus propias tareas,
por ejemplo, el panadero tiene “harina” y una de sus tareas es “amasar”, mientras que el
mecánico tiene “bujías” y una de sus tareas es “limpiar bujías”. Lo importante de todo esto es
que cada uno hace muy bien su tarea pero no tiene sentido “llevar el coche a la panadería de
la esquina” ni “pedir una baguette junto con un cambio de aceite”.
Vamos a estudiar unas pequeñas clases que nos servirán en un futuro para implementar un
ejercicio un poco más complicado. Estas clases son la clase “ficha” y la clase “tablero” que
nos servirán para implementar con el tiempo un juego de las “cuatro en raya”.
public class Fichas
{
String color;
public Fichas(String c)
{
color=c;
}
public String dameColor()
{
return(color);
}
}
Esta va a ser la clase ficha. Veámosla un poco: Esta clase tiene una variable “color” que es
un String. El tipo String no es un tipo primitivo en Java, es una clase que está dentro del API
de Java, mas concretamente dentro del paquete “Java.lang” y que además siempre se
incluye por defecto en cada programa Java que hagamos.
Por tanto, lo que estamos haciendo en la segunda línea de nuestro programa es declarar un
objeto sin valor de la clase String.
La tercera y la cuarta línea son dos métodos de la clase “Fichas” que estamos definiendo.
El primer método es un constructor. Un constructor es un método que se llama con la
sentencia “new”, es decir cuando alguien quiera crear un objeto y que nos define bajo que
condiciones se crea ese objeto, ya lo entenderás mejor. En este caso para que alguien
quiera crear una ficha tiene que pasar un objeto “String” como parámetro y obtendrá a
cambio un objeto de la clase “Fichas” del color que ha solicitado.
El segundo método nos devuelve un objeto “String” con el valor del color que tiene el objeto
ficha en ese momento.
public class Tablero
{
Fichas estadoTablero[][];
public Tablero()
{
estadoTablero=new Fichas [6][7];
}
public boolean verSiLlena(int indice)
{
return(estadoTablero[7][indice]==null);
}
}
Bueno, esta segunda clase que estudiamos tiene también un objeto interno llamado
“estadoTablero” que en este caso es un “array” cuyas posiciones son de la clase
anteriormente declarada “Fichas”.
También tenemos un “constructor” para el objeto “estadoTablero” que crea ese “array” con
las dimensiones que queremos.
Y en este caso hay una función que nos dice si la columna por la que nos interesamos con el
parámetro de entrada “índice” está llena.