2010-08-02 6 views
15

Je rencontre des problèmes lors de l'utilisation de plusieurs constructeurs dans java.Constructeur surchargé appelant un autre constructeur, mais pas en tant que première instruction

ce que je veux faire est quelque chose comme ceci:

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); 
    } 
} 

mais je ne peux pas, puisque le second constructeur ne peut pas appeler un autre constructeur, à moins qu'il est la première ligne.

Quelle est la solution courante pour une telle situation? Je ne peux pas calculer arg2 et arg3 "en ligne". Je pensais que peut-être créer une méthode d'assistance de la construction, qui fera la construction proprement dite, mais je ne suis pas sûr que ce soit si « joli » ...

EDIT: En utilisant une méthode d'assistance est également problématique puisque certains de mes champs sont définitifs, et je ne peux pas les définir en utilisant une méthode d'aide.

Répondre

21

Généralement, utilisez une autre méthode courante - une "aide à la construction" comme vous l'avez suggéré.

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 
    } 
} 

L'alternative est une approche de style usine dans laquelle vous avez un MyClassFactory qui vous donne MyClass cas, et MyClass a seulement un constructeur:

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); 
    } 
} 

Je préfère certainement la première option.

+1

Parfois, il peut être utile d'avoir une option pour lancer une exception et c'est une mauvaise habitude de la jeter des constructeurs. Pour ces situations, la méthode d'usine est meilleure. – Gaim

+3

Je préfère nettement la deuxième option, si vous mettez la méthode d'usine dans la classe elle-même plutôt que dans une classe différente. La première option ne vous permet pas de faire les champs que vous affectez avec les valeurs de paramètre 'final'. Le compilateur ne peut pas dire que la méthode 'init' est appelée une fois dans chaque constructeur, et jamais en dehors d'un constructeur. Si vous n'aimez pas la méthode d'usine, consultez ma réponse pour une autre option sur le calcul d'autres paramètres. – Jorn

+1

@Jorn Je suis avec Steve Yegge sur les usines, ils m'ont toujours agacé. –

9

La solution suivante est Factory method. Ces méthodes statiques peuvent être surchargées et après calcul, ils peuvent appeler le constructeur privé/protégé

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: Cette méthode est également idéal lorsque vous avez un champs final

0

Vous pouvez déplacer le code de MyClass(arg1, arg2, arg3) dans la méthode helper (nommez-le Init ou autre chose), puis appelez cette méthode dans les deux constructeurs.

+0

Je n'aime pas cette option, car elle ne vous permet pas de rendre définitifs les champs que vous affectez aux valeurs des paramètres. Le compilateur ne peut pas dire que la méthode 'init' est appelée une fois dans chaque constructeur, et jamais en dehors d'un constructeur. – Jorn

0

Vous pouvez créer un factory method qui appelle le constructeur:

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

au lieu de 'return new this (...)' cela devrait être 'return new MyClass (...)' – Jorn

+2

Cette solution est vraiment compliquée parce que parfois vous utilisez un appel direct (constructeur) et parfois indirect (méthode usine) – Gaim

+1

I D'accord: Dans ce cas, vous devriez également créer une méthode usine qui prend tous les trois arguments, et faire à la place le constructeur public 'public'' private'. – Jorn

4

Les options d'assistance et de l'usine sont très bons.

Il y a un autre:

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

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

Très sympa que vous pouvez utiliser des méthodes statiques là-bas. –

+0

yay pour la cession négative inexpliquée. Peut-être auriez-vous préféré la réponse de Jorn, qui est essentiellement la même? Ou vous êtes Jorn;) – Bozho

+2

Ce n'est pas moi, mais merci pour le vote de confiance. – Jorn

9

Bien que je préfère l'option de méthode de fabrication pointée par plusieurs autres réponses, je voulais proposer une autre option: Vous pouvez utiliser des méthodes statiques pour effectuer le calcul de vos autres paramètres :

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 bonne idée Jorn! –

0

Une autre façon est la suivante:

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

Comme une alternative aux réponses données, la manière la plus simple est de refactoriser le calcul de l'argument au constructeur à 3 arguments;

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

Utiliser les valeurs de marqueurs pour 'disparus'

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); 
} 
} 

Pour de meilleurs résultats, faire le constructeur 'général' protected ou private.

+0

+ sympa! J'aime toutes les idées créatives qui apparaissent ici. –

Questions connexes