24 de octubre de 2018

Clases

Ya hemos dicho que cada vez que nos hemos creado un archivo con File > New > Class estábamos creando una clase sin darnos cuenta. Sin embargo tenían muy poca funcionalidad, no hemos declarado nunca atributos y todos nuestros métodos los hemos marcado con el modificador static.

Las Clases van a servir para fabricar objetos de su tipo. Con ellas vamos poder encapsular el estado actual (atributos) y el comportamiento (métodos) de los objetos que vayamos creando (instanciando). Este encapsulamiento va a proteger a los miembros que lo necesiten de accesos no permitidos y va a ofrecernos la funcionalidad que corresponda con la responsabilidad de cada objeto. Así conseguimos tener valores agrupados que tienen relación entre ellos con una protección adecuada y un comportamiento que nos dará un valor añadido.

Con las Clases conseguimos tener valores agrupados que tienen relación entre ellos con un comportamiento y una protección adecuada que nos proporcionará un valor añadido


Si creamos una nueva clase con nombre MiClase y no marcamos la casilla para crear el método main, tendremos un archivo con este código:
  public class MiClase {

  }
Vamos a ver que significan los modificadores:
  • public: Significa que vamos a tener visibilidad (acceso) a esta clase desde otras partes del código (desde otros ámbitos/scope). Esto es importante pues al no haber creado un método main, no se ejecutará nada a menos que haya alguna llamada hasta esta clase desde un método main. Si no tuviera este modificador sólo sería accesible desde su propio package.
  • class: Define que es un tipo por referencia Clase. Recordad que hay más tipos por referencia, si pusiera interface en vez de class estaría declaran un tipo Interfaz.
Ahora vamos a definir algunos miembros (campos/atributos y métodos) de MiClase. Éste código aviso que va a ser un poco absurdo, pero es para ver conceptos. En la próxima sesión sobre Objetos ya usaremos un código de verdad funcional y con sentido. El código a usar es el siguiente:
  public class MiClase {

      int miNumero;
      private boolean miBoolean = true;
      protected String string1 = "Primer";
      public String string2; // mala practica
      protected static String string3;
    
      public int getMiNumero() {
          return miNumero;
      }
    
      public boolean isMiBoolean() {
          return miBoolean;
      }
    
      public String getString1() {
          return string1;
      }
    
      private String getString2() { // absurdo
          return string2;
      }

      void setString2(String string2) {
          this.string2 = string2;
      }

      public static String getString3() {
          return string3;
      }

      public static void setString3(String string3) {
          MiClase.string3 = string3;
      }
     
  }
Vamos a detallar qué estamos definiendo en el código:
  1. Atributos (Fields): Son declaraciones de variable al nivel raíz del cuerpo de la clase. Podemos hacer distinción entre variables de instancia y de clase:
    1. Variables de instancia: Atributos que tendrá cada objeto (instancia) que creemos de esta clase.
    2. Variable de clase: Son variables que no necesitan de ninguna instancia pues pertenecen a la clase en sí misma y es un valor único. Para declarar este tipo de variables se usa el modificador static (lo vemos en la declaración de string3)
  2. Hay nuevos modificadores que nos permiten proteger a los miembros estableciendo un control de acceso:
    1. private: Sólo se tiene acceso desde la propia clase.
    2. protected: Sólo se tiene acceso desde los subtipos y su paquete (package), además del acceso private.
    3. public: Igual que cuando la clase, vamos a tener acceso desde cualquier ámbito.
    4. (no modifier): Si nos fijamos en la declaración de miNumero o de setString2, vemos que no tienen ningún modificador. Si no se pone ninguno se entiende que es privado para el package (package-private) además de la visibilidad private. Todos estos modificadores pueden verse en la documentación de Java.



  3. Hay variables que están inicializadas porque tienen asignado un valor como miBoolean y string1. Estos valores son valores por defecto y cada objeto se hará una copia de ellos. Así, los otros Strings tendrán el valor null que es el valor por defecto para los tipos por referencia, pero string1 tendrá el valor "Primer".
  4. Hemos definido métodos al nivel raíz de la clase pero ahora sin el modificador static como siempre habíamos puesto hasta ahora (pues todos eran métodos de clase). Estos métodos son métodos de instancia (necesitan de un objeto) y se les conoce como miembros igual que a los atributos
  5. Los métodos para acceder a los atributos comienzan por get. En general se llaman "getters" y por ahora sólo devuelven el valor que corresponde.
  6. El getter de la variable miBoolean empieza por "is" en vez de "get". Para los métodos que devuelven booleanos ya dijimos que es habitual preguntar es/estaAlgo()en inglés como no hay diferencia se verá habitualmente "isSomething"
  7. Los métodos para asignar valores a las variables se llaman "setters", empiezan por set y reciben el valor para asignar.
  8. En el método setString2(String string2) usamos la palabra reservada this. Es necesario usarla debido a que en el ámbito/scope de éste método el parámetro string2 está ocultando el atributo string2. Con this nos referimos al objeto en sí mismo y usando el punto (.) accedemos a sus miembros, en este caso a su atributo string2. De esta forma podemos asignar al atributo string2 el valor que le pasemos como argumento aunque el parámetro y el atributo tengan el mismo identificador.
  9. Sin embargo a los miembros de clase (static) se accede desde el nombre de la clase en vez de usar el nombre de la variable como se puede ver en el método setString3.
Existen publicaciones que consideran que los getters y setters (denominados más recientemente como accesors) son el mal y tratan a los objetos como meros recipientes de datos alejándonos de la POO. Desde mi punto de vista es pura semántica, simplemente cómo llamar a las cosas no te hará cambiar tu forma de pensar, y lo que recomiendo es que no se creen si no son necesarios y seguros. Ya iremos viendo como decidir esto último. También recomiendo siempre proteger las variables al máximo (en general private) y una vez creados los getters y setters usarlos siempre, incluso dentro de la propia clase (si quiero incorporar lógica en esas operaciones sólo debo cambiar el método que corresponda)
En el vídeo puedes ver la demostración en vivo de todo esto y lo siguiente, cómo usarlo en un main en otro archivo:


¿Si no hay "main" cómo usamos MiClase?

Evidentemente tendrá que haber un método main que la llame desde fuera del archivo MiClase.java. Vamos a crearnos otra clase llamada Ejecucion con un main y el siguiente código:
  public class Ejecucion {

      public static void main(String[] args) {
        
          MiClase miObjeto = new MiClase(); // creo una instancia
        
          System.out.println("miNumero es " + miObjeto.miNumero);
//          System.out.println("miBoolean es " + miObjeto.miBoolean); // sin acceso
          System.out.println("miBoolean es " + miObjeto.isMiBoolean());
        
          System.out.println("string1 es " + miObjeto.string1);
          System.out.println("string1 tiene longitud " + miObjeto.string1.length());
        
//          System.out.println("string2 es " + miObjeto.getString2(); // absurdo
          System.out.println("string2 es " + miObjeto.string2);
        
          MiClase.setString3("string-3");
//          System.out.println("string3 es " + miObjeto.getString3()); // warning
          System.out.println("string3 es " + MiClase.getString3());
      }

  }
Aquí vemos que hay miembros que podemos ver y otros que no. Esto es controlado por el acceso que demos al código con los modificadores antes explicados. Para acceder a los miembros uso la notación por punto (.), así si quiero acceder al atributo miNumero del objeto referenciado por mi variable miObjeto usaré miObjeto.miNumero. Para los métodos es igual solo que añadiré a la invocación los paréntesis para cumplir con la firma que corresponda. En el vídeo hago unos cuantos cambios para verlo en profundidad.

Accedemos a los miembros de un objeto con el punto (.) limitados por el control de acceso que marquemos (public, protected, ...)


Aunque hemos construido un objeto del tipo MiClase todavía no hemos visto cómo personalizar su construcción. A falta de un constructor específico en MiClase para crear objetos, Java siempre tiene un constructor por defecto que creará una instancia y asignará los valores por defecto que encuentre.

En la siguiente sesión trabajamos con varios objetos y diferentes constructores.

1 comentario:

Compárteme

Entradas populares