2012-09-22 76 views
5

Tengo dos objetos que usan métodos muy similares, excepto una línea. Por ejemplo:Creación de un nuevo objeto en la clase abstracta en Java

public class Cat extends Animal 
public class Dog extends Animal 

Y ambos utilizan un método breed en la clase abstracta Animal. Uno llama al new Dog(), y el otro new Cat(). En este momento solo lo declaro como abstract public void breed(); en Animal, pero ¿hay alguna manera de generalizarlo, así que no tengo que convertirlo en un método abstracto para anular?

+0

sí, no es necesario que todos los métodos en una clase tienen que ser abstracta –

+1

@SashiKant Aunque es verdad, no creo que eso es lo que el PO estaba pidiendo ... –

Respuesta

3

Hay muchas formas de hacerlo, asumiendo que por breed quiere decir "crear hijos de mi parte".

Reflexión

primera es utilizar la reflexión. Si usted tiene un constructor sin argumentos para sus clases, esto es tan fácil como llamar Class.newInstance:

public Animal breed() { 
    try { 
     return (Animal) getClass().newInstance(); 
    } catch (Exception ex) { 
     // TODO Log me 
     return null; 
    } 
} 

Si usted no tiene un constructor sin argumentos en todas sus subclases, usted tiene que tener una constructor uniforme en todas sus subclases. Por ejemplo, si usted tiene Cat(int, String) y Dog(int, String), entonces usted necesita para obtener el constructor a través de Class.getConstructor e invocar newInstance en que:

return (Animal) getClass().getConstructor(int.class, String.class).newInstance(0, "Unnamed"); 

int y String aquí puede ser la edad y el nombre, por ejemplo. Así es como haces esto con reflexión.

Proveedores

Otra forma es utilizar esta interfaz sencilla:

public interface Provider<T> { 
    T create(); 
} 

Después haga que su clase abstracta tomar un ejemplo de esto en su constructor:

public abstract class Animal { 
    private final Provider<Animal> animalProvider; 

    protected Animal(... , Provider<Animal> animalProvider) { 
     // ... 
     this.animalProvider = animalProvider; 
    } 

    public Animal breed() { 
     return animalProvider.create(); 
    } 
} 

Luego, su las subclases pasarán un Provider<Animal> a la superclase que creará nuevas instancias de la subclase:

public class Dog extends Animal { 
    public Dog(...) { 
     super(... , new DogProvider()); 
     // ... 
    } 

    private static class DogProvider implements Provider<Animal> { 
     public Animal create() { 
      return new Dog(...); 
     } 
    } 
} 

Haz lo mismo para otras subclases.

Nota: si por breed quiere decir "obtener el tipo de mí", entonces debe editar su pregunta para decirlo. Si esto es lo que quería decir, entonces esta es una solución viable:

public abstract class Animal { 
    protected final Breed breed; 

    protected Animal(... , Breed breed) { 
     // ... 
     this.breed = breed; 
    } 

    public Breed getBreed() { 
     return breed; 
    } 
} 

Recomiendo seguir las get/set convenciones para los métodos de contenedores de datos. Java tiene clases de beans diseñadas para manejar estas convenciones de nomenclatura, y es más o menos un estándar en muchas plataformas. Para sus subclases:

public class Dog extends Animal { 
    public Dog(...) { 
     super(... , new Breed(...)); 
     // ... 
    } 
} 
2

No, no hay. Usted tendrá que tener algo como lo que ha hecho de la siguiente manera

Creo que desea tener en su clase abstracta

public abstract Breed getBreed(); 

y luego en cada subclase tienen

public Breed getBreed() { 
    return new DogBreed(); 
} 

y una un gato que regresa similar.

o

tienen un campo protegido en la clase de animal llamado raza. Esto podría entonces inicializarse en cada una de las subclases. Esto eliminaría la necesidad de un método abstracto. Por ejemplo

public abstract class Animal { 
    Breed breed; 
... 
} 

y luego en el perro tiene

public class Dog extends Animal { 
    public Dog() { 
     breed = new DogBreed(); 
    } 
} 

y tener algo similar a la del gato.

Podría valer la pena que al mismo tiempo que pasa en la raza a la ctor perro/gato para que pueda crear objetos para perros de diferentes razas en lugar de restringir su modelo a una sola raza de perro

No estoy seguro La casta se modela necesariamente correctamente en tu ejemplo. ¿Realmente quieres que el nuevo Dog() sea una raza? ¿O quieres decir tipo? En ese caso, es solo un animal y el método abstracto para devolver animales es el camino a seguir.

3

En realidad, sí se puede. Es necesario utilizar la reflexión lo que el rendimiento podría ser un poco dudoso, pero esto (no probado) debería funcionar:

public abstract class Animal{ 
    public Animal breed(){ 
     return getClass().newInstance(); 
    } 
    //other methods 
} 

Esto devolverá una nueva instancia del tipo de llamada real, no el tipo de animal (donde se implementa) .

Esto es realmente algo similar al Prototype Pattern. Aunque en este caso está creando una nueva instancia, no copia una instancia existente.

Editar

Como @FrankPavageau señaló en los comentarios, en lugar de enmascarar una excepción en el constructor, se puede lograr el mismo resultado utilizando

public abstract class Animal{ 
    public Animal breed(){ 
     return getClass().getConstructor().newInstance(); 
    } 
    //other methods 
} 

Qué va a envolver cualquier excepción lanzada en un InvocationTargetException que es un poco más limpio y probablemente más fácil de depurar. Gracias @FrankPavageau por esa sugerencia.

+0

intentado esto, pero arrojó 'InstantiationException'. –

+0

@ Eng.Fouad, ¿lo arrojó o dijo que tenía que atraparlo? Hay varios ejemplos de esto trabajando en la web. –

+0

Sí, arroja 'InstantiationException'. Puedes probarlo tú mismo :) –

Cuestiones relacionadas