2010-08-02 8 views
15

Tengo problemas para usar varios constructores en Java.Constructor sobrecargado llamando a otro constructor, pero no como primera instrucción

lo que yo quiero hacer es algo como esto:

public class MyClass { 

// first constructor 
public MyClass(arg1, arg2, arg3) { 
    // do some construction 
} 

// second constructor 
public MyClass(arg1) { 
     // do some stuff to calculate arg2 and arg3 
     this(arg1, arg2, arg3); 
    } 
} 

pero no puedo, ya que el segundo constructor no puede llamar a otro constructor, a menos que sea la primera línea.

¿Cuál es la solución común para tal situación? No puedo calcular arg2 y arg3 "en línea". Pensé que quizás crear un método de ayuda de construcción, que haría la construcción real, pero no estoy seguro de que sea tan "bonita" ...

EDIT: Usar un método de ayuda también es problemático ya que algunos de mis campos son finales, y no puedo configurarlos usando un método de ayuda.

Respuesta

21

Por lo general, utilice otro método común: un "ayudante de construcción", como ha sugerido.

public class MyClass { 

    // first constructor 
    public MyClass(arg1, arg2, arg3) { 
     init(arg1, arg2, arg3); 
    } 

    // second constructor 
    public MyClass(int arg1) { 
     // do some stuff to calculate arg2 and arg3 
     init(arg1, arg2, arg3); 
    } 

    private init(int arg1, int arg2, int arg3) { 
     // do some construction 
    } 
} 

La alternativa es un enfoque al estilo de la fábrica en la que usted tiene una MyClassFactory que le da MyClass casos, y MyClass tiene sólo el constructor:

public class MyClass { 

    // constructor 
    public MyClass(arg1, arg2, arg3) { 
     // do some construction 
    } 
} 

public class MyClassFactory { 

    public static MyClass MakeMyClass(arg1, arg2, arg3) { 
     return new MyClass(arg1, arg2, arg3); 
    } 

    public static MyClass MakeMyClass(arg1) { 
     // do some stuff to calculate arg2 and arg3 
     return new MyClass(arg1, arg2, arg3); 
    } 
} 

definitivamente prefiero la primera opción.

+1

A veces puede ser útil tener una opción para lanzar una excepción y es un mal hábito arrojarla de los constructores. Para estas situaciones, el método de fábrica es mejor – Gaim

+3

Definitivamente prefiero la segunda opción, si pones el método de fábrica en la clase en lugar de en una clase diferente. La primera opción no le permite hacer los campos que asigna con los valores de parámetro 'final'. El compilador no puede decir que el método 'init' se llama una vez en cada constructor, y nunca fuera de un constructor. Si no le gusta el método de fábrica, consulte mi respuesta para otra opción sobre el cálculo de otros parámetros. – Jorn

+1

@Jorn Estoy con Steve Yegge en las fábricas, siempre me han molestado. –

9

La siguiente solución posible es Factory method. Estos métodos estáticos pueden ser sobrecargados y después del cálculo pueden llamar al constructor privado/protegido

public class MyClass { 

    private MyClass(arg1, arg2, arg3) { 
     // do sth 
    } 

    public static MyClass getInstance(arg1) { 
     // calculate arg2,3 
     return new MyClass(arg1, arg2, arg3); 
    } 

    public static MyClass getInstance(arg1, arg2, arg3) { 
     return new MyClass(arg1, arg2, arg3); 
    } 
} 

EDIT: Este método también es ideal cuando se tiene un final campos

0

Puede mover el código de MyClass(arg1, arg2, arg3) en el método de ayuda (nómbrelo Init o algo más) y luego llame a este método en ambos constructores.

+0

No me gusta esta opción, porque no le permite hacer que los campos asignados con los valores de los parámetros sean finales. El compilador no puede decir que el método 'init' se llama una vez en cada constructor, y nunca fuera de un constructor. – Jorn

0

Se puede crear un factory method que llama al constructor:

public class MyClass { 

    // first constructor 
    public MyClass(arg1, arg2, arg3) { 
    // do some construction 

    } 

    // second constructor as factory method 
    public static createMyClassAndDoFunkyStuff(int arg1) { 
     // do some stuff to calculate arg2 and arg3 
     return new MyClass(arg1, arg2, arg3); 
    } 

} 
+0

en lugar de 'return new this (...)' debe ser 'return new MyClass (...)' – Jorn

+2

Esta solución es realmente desordenada porque a veces se usa llamada directa (constructor) y a veces indirecta (método de fábrica) – Gaim

+1

I de acuerdo: en este caso, también debe crear un método de fábrica que tome los tres argumentos, y haga que el ahora 'public' constructor' private' en su lugar. – Jorn

4

El ayudante de fábrica y opciones son muy buenas.

hay otro:

public MyClass(int arg1) { 
    this(arg1, calculateArg2(), calculateArg3()); 
} 

private static int calculateArg2() {..} 
private static int calculateArg3() {..} 
+0

Es muy bueno que puedas usar métodos estáticos allí. –

+0

yay por el downvote inexplicado. ¿Tal vez habrías bajado la respuesta de Jorn, que es esencialmente la misma? O eres Jorn;) – Bozho

+2

No soy yo, pero gracias por el voto de confianza. – Jorn

9

Aunque prefiero la opción método de fábrica apuntado por varias otras respuestas, quería sugerir otra opción: Puede utilizar los métodos estáticos para hacer el cálculo de sus otros parámetros :

public class MyClass { 
    public MyClass(int arg1, int arg2, int arg3) { 
     // do some construction 
    } 

    public MyClass(int arg1) { 
     //call to this() must be the first one 
     this(arg1, calculateArg2(arg1), calculateArg3()); 
     //you can do other stuff here 
    } 

    private static int calculateArg2(int arg1) { 
     //calc arg2 here 
    } 

    private static int calculateArg3() { 
     //calc arg3 here 
    } 
} 
+0

+1 buena idea Jorn! –

0

Otra forma es la siguiente:

public class MyClass { 

    // first constructor 
    public MyClass(arg1, arg2, arg3) { 
    // do some construction 
    doSomeStuffToArg3Arg3(arg2, arg3) 
    } 

    // second constructor 
    public MyClass(int arg1) { 
     this(arg1, arg2, arg3); 
    } 

    private void doSomeStuffToArg3Arg3(int arg2, int arg3) { 
    // do some stuff to calculate arg2 and arg3 
    } 
} 
0

Como una alternativa a las respuestas dadas, la manera más simple es refactorizar el cálculo del argumento al constructor de 3 argumentos;

public class MyClass { 

    // first constructor 
    public MyClass(arg1, arg2, arg3) { 
     if (null == arg2) { 
      // calculate arg2 
     } 
     if (null == arg3) { 
      // calculate arg3 
     } 
     // do some construction 
    } 

    // second constructor 
    public MyClass(arg1) { 
     this(arg1, null, null); 
    } 
} 
3

valores de uso de marcadores para 'perdido'

public class MyClass { 
public MyClass(arg1, arg2, arg3) { 
    // do some stuff to calculate arg2 and arg3 if they are the missing values 
    // do some construction 
} 
public MyClass(arg1) { 
    this(arg1, null, null); 
} 
} 

Para obtener los mejores resultados, hacer que el constructor 'general' o protectedprivate.

+0

+ ¡agradable! Me gusta toda la idea creativa que aparece aquí. –

Cuestiones relacionadas