Autor Tema: Duda al usar clases abstractas en Java  (Leído 8000 veces)

gatoher

  • Principiante
  • **
  • APR2.COM
  • Mensajes: 86
    • Ver Perfil
Duda al usar clases abstractas en Java
« en: 30 de Julio 2014, 23:38 »
Hola que tal soy nuevo en el foro un saludos a todos. Estoy estudiando Java y he llegado a las clases abstractas y interfaces. Y la verdad que tengo una duda o veo una pega en su uso y quería saber si hay una forma de solucionarlo o es que no las utilizo donde no debo.
Haber pongo un ejemplo sencillo de una clase abstracta de personas, y clases que heredan de ella como medico, enfermero etc ...

En las clase abstracta pogo metodos y propiedades comunes a una persona como dni, nombre, apellidos, y metodos que si voy a utilizar en las clases que heredan como calcular salario, etc  .... Luego en cada clase que hereda de persona, "es donde empiezo a ver las pegas" pongo propiedades expecificas como jornal, instrumental, horarios y metodos expecificos a cada una de las clases que no estan declarados en la clase abstracta.

Bueno, pues cuando aplico la polimorfia, osea hago un medico sea una persona  :) resulta que ya no puedo haceder a sus propiedades ni metodos "los que tiene su clase en particular". Se puede acceder a ellas pero haciendo "moldes", eso si, pero si metes a los medicos y enfermeros en un array o una lista de personas al recorrerlas tienes que hacer un instace of, etc ...

Entonces, una solución a este problema, o es que no tengo claro donde aplicar las clases abstractas.

Tenía una segunda duda sobre la diferencia entre clases abstractas y interfaces. Se que las interfaces son completamente abstractas sus métodos y sus constantes, y que una clase puede heredar de varias interfaces. Miéntras que una clase abstracta puede tener métodos abstractos y no abstractos, ademas de variables de instancia, pero que una clase solo puede heredar de una clase abstracta. Entonces, ¿Cuando usar una o la otra?

Bueno, la verdad que me he estendido bastante para ser mi primer post, pero la verdad que llevo tiempo con estas dudas, si alguién me lo pudiera explicar pues se lo agradeceria.

Salludos

Alex Rodríguez

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 2050
    • Ver Perfil
Re:Duda al usar clases abstractas en Java
« Respuesta #1 en: 31 de Julio 2014, 00:29 »
Hola, igualmente saludos y bienvenido. Lo primero que te recomendaría es que revisaras los conceptos tal y como se explican en el curso Java desde cero que tenemos en esta url: http://aprenderaprogramar.com/index.php?option=com_content&view=category&id=68&Itemid=188

En relación a las cuestiones que comentas y tratando de ser breve:

En un esquema de herencia tienes acceso a los métodos de las superclases por ser públicos. A través de los métodos get puedes acceder a los atributos.

En principio no tendrás acceso a los atributos por ser private. Pero si lo deseas, puedes declararlos como protected. Este modificador de acceso permite que las subclases conozcan los atributos de las superclases.

Cuando te refieres a que al aplicar la polimorfia no puedes acceder... : puedes acceder a todo lo que sea común (definido en las superclases). Si quieres acceder a métodos propios de una subclase, necesitarás identificar el tipo exacto con el que estás trabajando usando instance of o de otras maneras.

Para saber cuándo aplicar clases abstractas mira el curso.

Sobre la diferencia entre clases abstractas e interfaces: todo se mueve en niveles de abstracción. El máximo nivel de abstracción viene dado por las interfaces. Una clase abstracta podemos verlo como un nivel intermedio de abstracción. Y una clase concreta es el nivel más bajo de abstracción. El manejo de estos conceptos es un poco confuso sobre todo cuando no se tiene experiencia trabajando con ellos. A medida que vayas realizando el diseño de programas usándolos posiblemente tú mismo irás viendo qué es lo más conveniente.

Se hace quizás un poco complicado hablar sobre conceptos, quizás poniéndote ejemplos o viendo el código creado por otras personas, al ser algo aplicado y práctico, también puede ayudar. Si pegas el código que has creado (para ello pulsa el botón # del foro y al aparecer las etiquetas [ code ] ... [ / code] e indicas dónde tienes los problemas podemos tratar de revisarlo de forma más práctica.

Saludos.

gatoher

  • Principiante
  • **
  • APR2.COM
  • Mensajes: 86
    • Ver Perfil
Re:Duda al usar clases abstractas en Java
« Respuesta #2 en: 31 de Julio 2014, 21:35 »
Hola Alex, ante todo gracias por responder. Mira voy a poner parte de código, "quitare lo superfluo" de un ejercicio del instituto, es básico y todo el mundo creo lo entendera.

Es un ejercicio para una gestora de una inmobiliaria,"el ejercicio con frames", por lo que podía venderse pisos, chalets, aticos etc. Es obvio que la mayoria de inmuebles comparten muchas propiedades, pongo una captura de la clase abstracta que hice;

Código: [Seleccionar]
abstract class Inmobiliaria {
   protected String nombre;
   protected String direccion;
   protected float metros;
   protected int dormitorios;
   protected String estado;
   protected static int cod = 0;
 
   //constructor
   public Inmobiliaria(String nombre, String direccion, float metros, int dormitorios, String estado) {
        this.nombre = nombre;
        this.direccion = direccion;
        this.metros = metros;
        this.dormitorios = dormitorios;
        this.estado = estado;
        cod++;
     
    }
   
   
 
   protected abstract double precio();
   

Como se puede ver pongo unas propiedades que van a compartir todos los inmuebles y un metodo abstracto que también va a ser usado, "calcular precio".
Luego cree un frame en el que a la hora de comprar un inmueble se pedian estos datos,
más lo específicos de cada tipo. Pongo el código de por ejemplo pisos.

Código: [Seleccionar]
public class Pisos extends Inmobiliaria{
   
    private String ubicacion;
    private String balcon;
    private int planta;
    private static final float pvpMetro = 300;
    //constructor
    public Pisos(String nombre, String direccion, float metros, int dormitorios, String estado,String ubicacion, String balcon, int planta) {
        super(nombre, direccion, metros, dormitorios, estado);
     
        this.ubicacion = ubicacion;
        this.balcon = balcon;
        this.planta = planta;
    }

    @Override
    protected double precio() {
    double precioTotal = 0;
     
      precioTotal = this.metros*pvpMetro;
     
      return precioTotal;
    }
   
    public boolean ascensor(){
       
        boolean ascensor = false;
       
        //implementación
        return ascensor;
    }




Como se puede ver unas propiedades "específicas" para pisos, las propiedades private a la clase, se implementa el método abstracto de la clase abstracta y hay un método "un poco tonto pero sirve de ejemplo" ascensor que nos va a decir si tiene ascensor o no. Hay que fijarse que es publico y todo.

Hasta aqui todo perfecto, ahora viene cuando a viene mi sorpresa. ¿Donde almacenamos todos los inmuebles, en un Array, vector, etc de la clase abstracta Inmobiliaria, o hago un Array para cada tipo de inmueble?

Bueno, yo al hacer el ejercicio pense que ya que uno hace una clase abstracta, lo suyo sería tener un Array de la clase inmobiliaria, introducir en ella todo tipo de inmuebles y luego en el Frame de la aplicación poder elegir que tipo de inmueble se quiere ver, dependiendo del tipo se verían todas sus propiedades, las específicas y las "comunes".

Perfecto al hacerlo me encontre que no podía acceder desde un objeto Inmobiliaria ni a las propiedades ni métodos propios de cada tipo de inmueble, para hacer a ellos tenia que hacer esto;

Código: [Seleccionar]
               /// MOLDE para acceder a las propiedades de cada tipo de inmueble

               String lugar = String.valueOf(((Piso)obj).getUbicacion());
              label36.setText(peso);
              int  altura =  String.valueOf(((Piso)obj).getPlanta());
              label37.setText(pulgadas);

Al método de saber si tiene ascensor ni siendo publico he podído acceder. Me lleve una sorpresa la verdad.
Posible solución que mis compañeros hicieron, en lugar de crear un Array de la clase abstracta inmobiliaria crearon un Array para cada tipo de inmueble,entonces si se puede acceder a las propiedades y métodos de cada clase. A mi esa solución no me terminaba de convencer, pues entonces para eso yo no hubiera hecho una clase abstracta.

Mi duda es, ¿como se podría haber hecho el ejercicio para evitar el poblema de no poder acceder a las propiedades específicas de cada clase?
¿Hay alguna solución a este problema?
¿La solución hubiera sido una interfaz o varias?

Alex Rodríguez

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 2050
    • Ver Perfil
Re:Duda al usar clases abstractas en Java
« Respuesta #3 en: 01 de Agosto 2014, 00:05 »
Hola, lo primero que no veo claro es por qué declaras abstract class Inmobiliaria en lugar de public abstract class Inmobiliaria

Después tengo que decirte que tus preguntas van en la buena dirección: estás viendo que hay una falta de coherencia como esa creación de un Array para cada tipo de inmueble.

El problema que veo, no queriendo criticar sino ayudar, es que lo que ocurre en el fondo es que están manejando conceptos que no acaban de entender y eso es algo así como pretender caminar en la oscuridad sin darse golpes contra las paredes. Sería más razonable buscar una linterna y aprender cómo usarla para poder avanzar. Inicialmente te puede retrasar un poco respecto a los compañeros que avanzan dándose porrazos, pero en cuanto aprendas a usar la linterna los alcanzarás y dejarás atrás. Eso en Java vendría siendo como aprender las bases de Java, por eso te hacía referencia a lo importante que es un curso sobre los fundamentos de Java.

Hablar de fundamentos llevaría mucho tiempo y eso es una tarea que tendrás que pensar tú mismo si acometes. Centrándonos en el problema que comentas, los objetos Inmobiliaria sí pueden y deben conocer métodos... Para ello se utilizan distintas técnicas, quizás es largo de explicar y ver algo de código sea más útil.

Aquí te dejo un ejemplo de cómo se puede acceder a métodos de subclases de una clase abstracta, básicamente dos técnicas (habría más, pero creo que éstas son válidas): sobreescritura de métodos abstractos y uso de instanceof. En el caso de sobreescritura, el compilador determina dinámicamente qué método es el que debe aplicar. Con instanceof, debes hacer casting para acceder al método de interés. Espero te sirva de ayuda...

CLASE INMOBILIARIA

Código: [Seleccionar]

public abstract class Inmobiliaria {
   protected String nombre;
   protected String direccion;
   protected float metros;
   protected int dormitorios;
   protected String estado;
   protected static int cod = 0;
 
   //constructor
   public Inmobiliaria(String nombre, String direccion, float metros, int dormitorios, String estado) {
        this.nombre = nombre; this.direccion = direccion;
        this.metros = metros; this.dormitorios = dormitorios;
        this.estado = estado; cod++;
    }
   
   protected abstract double precio();
   protected abstract boolean ascensor();
}


CLASE PISO
Código: [Seleccionar]
public class Piso extends Inmobiliaria{
   
    private String ubicacion;
    private String balcon;
    private int planta;
    private static final float pvpMetro = 300;
    //constructor
    public Piso(String nombre, String direccion, float metros, int dormitorios, String estado,String ubicacion, String balcon, int planta) {
        super(nombre, direccion, metros, dormitorios, estado);   
        this.ubicacion = ubicacion;
        this.balcon = balcon;
        this.planta = planta;
    }

    @Override
    protected double precio() {
    double precioTotal = 0;
      precioTotal = this.metros*pvpMetro;
      return precioTotal;
    }
   
    public boolean ascensor(){
        System.out.println ("Soy un ascensor piso - método sobreescrito");
        boolean ascensor = false;
        return ascensor;
    }
   
    public boolean ascensor2(){
        System.out.println ("Soy un ascensorian2 de piso - Método sólo de piso");
        boolean ascensor2 = false;
        return ascensor2;
    }
}

CLASE PISOS2
Código: [Seleccionar]
public class Pisos2 extends Inmobiliaria{
   
    private String ubicacion2;
    private String balcon2;
    private int planta2;
    private static final float pvpMetro = 300;
    //constructor
    public Pisos2(String nombre, String direccion, float metros, int dormitorios, String estado,String ubicacion, String balcon, int planta) {
        super(nombre, direccion, metros, dormitorios, estado);
        this.ubicacion2 = ubicacion;
        this.balcon2 = balcon;
        this.planta2 = planta;
    }

    @Override
    protected double precio() {
    double precioTotal = 0;     
      precioTotal = this.metros*pvpMetro;   
      return precioTotal;
    }
   
    public boolean ascensor(){
        System.out.println ("Soy un ascensor pisos2");
        boolean ascensor = false;       
        return ascensor;
    }
   
    public boolean dimeTrue() { return true;}   
}

CLASE TEST
Código: [Seleccionar]
import java.util.ArrayList;
import java.util.Iterator;
public class Test{
    public static void main (String [] args) {
        Inmobiliaria i1 = new Piso("juan", "calle 1", 3.22f, 4, "vendido", "norte", "si", 2);
        Inmobiliaria i2 = new Pisos2("jose", "calle flu", 6.22f, 2, "comprado", "sur", "no", 7);
        i1.ascensor();
        i2.ascensor();
        System.out.println ("Hemos accedido a los métodos ascensor de objetos Inmobiliaria...");
        ArrayList <Inmobiliaria> lista1 = new ArrayList<Inmobiliaria> ();
        lista1.add(i1);
        lista1.add(i2);
        Iterator<Inmobiliaria> it = lista1.iterator();
        while ( it.hasNext() ) {           //Utilizamos el método hasNext de los objetos tipo Iterator
            Inmobiliaria m1 = it.next();

            System.out.println ("Analizando elemento... " );
            m1.ascensor();                //Utilizamos el método next de los objetos tipo Iterator
            if (m1 instanceof Piso) {((Piso)m1).ascensor2();} else {
                System.out.println ("El objeto analizado no es tipo Piso");
            }
        }   
    } 
}

Pequeño comentario: la clase Piso y la clase Pisos2 heredan de la clase abstracta inmobiliaria.

En la clase test creamos un array de objetos Inmobiliaria y accedemos a los métodos propios de Piso y Pisos2...

gatoher

  • Principiante
  • **
  • APR2.COM
  • Mensajes: 86
    • Ver Perfil
Re:Duda al usar clases abstractas en Java
« Respuesta #4 en: 01 de Agosto 2014, 00:34 »
Hola Alex, ya veo como has accedido a los metodos privados. Lo has declarado en la clase abstracta y luego sobreescrito en las subclases, el problema de hacerlo asi es que tienes que imprementar los metodos de la clase abstracta en todas las subclases que hereden de ella, ¿que pasa si el método ancensor solo se utiliza en la clase piso? Como en este ejemplo, el resto de las clases no lo utilizan, eso es un problema.
La segunda forma que has puesto es haciendo un molde, asi es como yo accedia a los métodos.

Yo creo que falla es el diseño, habría que crear clases abstractas que heredasen de Inmobiliaria insertando metodos que fueran comunes a subclases"hasta donde fuera posible".

De manera que la primera clase abstracta casi solo tuviera las propiedades y las subclases los metodos.

Tal vez es cuestión de sentarse a pensar en maquetar antes de ponerse a la tecla.

Gracias por responder, un saludo

 

Sobre la educación, sólo puedo decir que es el tema más importante en el que nosotros, como pueblo, debemos involucrarnos.

Abraham Lincoln (1808-1865) Presidente estadounidense.

aprenderaprogramar.com: Desde 2006 comprometidos con la didáctica y divulgación de la programación

Preguntas y respuestas

¿Cómo establecer o cambiar la imagen asociada (avatar) de usuario?
  1. Inicia sesión con tu nombre de usuario y contraseña.
  2. Pulsa en perfil --> perfil del foro
  3. Elige la imagen personalizada que quieras usar. Puedes escogerla de una galería de imágenes o subirla desde tu ordenador.
  4. En la parte final de la página pulsa el botón "cambiar perfil".