11
votes

Comment créer un "champ abstrait"?

Je sais que les champs abstraits n'existent pas en Java. Je lis aussi Cette question mais les solutions proposées ne résoudront pas mon problème. Peut-être qu'il n'y a pas de solution, mais il convient de demander :)

problème

J'ai une classe abstraite qui fait une opération dans le constructeur en fonction de la valeur de l'un de ses des champs. Le problème est que la valeur de ce champ changera en fonction de la sous-classe . Comment puis-je faire pour que l'opération soit effectuée sur la valeur du champ redéfini par la sous-classe?

si je viens de "remplacer" le champ de la sous-classe, l'opération est effectuée sur la valeur du champ dans la classe abstraite.

Je suis ouvert à toute solution qui serait veiller à ce que l'opération soit effectuée lors de l'instanciation de la sous-classe (c.-à-d. Publier l'opération dans une méthode appelée par Chaque sous-classe du constructeur n'est pas une solution valide, car une personne peut prolonger la classe abstraite et oublier d'appeler la méthode).

En outre, je ne veux pas donner la valeur du champ comme un argument du constructeur.

existe-t-il une solution pour le faire ou devrais-je simplement changer de conception?


EDIT:

Mes sous-classes sont en fait Quelques outils utilisés par mon programme principal, le constructeur doit donc être public et prendre exactement les arguments avec lesquels ils seront appelés: xxx

(Les sous-classes sont la main, le crayon et AddObject que tous étendent la classe abstraite Outil)

C'est pourquoi je ne veux pas changer le constructeur.

La solution que je suis sur le point d'utiliser est de changer légèrement le code ci-dessus à: < Pré> xxx

et utilisez un getter abstrait pour accéder au champ.


4 commentaires

Est-ce que ce champ de sous-classe est une constante? Sinon, comment passez-vous la valeur souhaitée?


Je suppose que la conception est défectueuse de toute façon parce que, en raison de l'opération du constructeur, vous aurez probablement du mal à tester avec cette classe. Voir YouTube.com/watch?v=rlflcwkxhj0 Pour une bonne explication.


Super vidéo, je vais essayer de nettoyer mes constructeurs maintenant :)


Assurez-vous d'accepter une réponse puisque les gens peuvent être hésitants pour vous aider à l'avenir.


8 Réponses :


0
votes

Je pense que vous avez besoin d'une usine (alias "constructeur virtuel") qui peut agir sur ce paramètre.

S'il est difficile de faire dans une langue donnée, vous y réfléchissez probablement de manière incorrecte.


0 commentaires

14
votes

Que diriez-vous de Abstract Getter / Setter pour le champ? XXX


5 commentaires

Les champs de sous-classe sont initialisés une fois le super constructeur exécuté, cela ne fonctionnera donc pas comme prévu.


Cela fonctionne réellement si vous n'utilisez pas de champ mais mettez la valeur directement comme la valeur de retour du "getter" comme dans le code de code donné (donc ce n'est plus un getter approprié ...) Mais merci, c'est mieux que rien , Si aucune autre réponse ne vient, je l'utiliserai!


N'est-ce pas parce que c'est une constante alors? Le compilateur fait probablement quelque chose d'intelligent avec elle. Tout le monde sait?


L'utilisation de @overrdie annotations peut être dangereuse pour la compilabilité de votre application


Je vais utiliser une initialisation en deux étapes comme suggérée par SB, ainsi qu'un getter abstrait comme suggéré par Crozin. De cette façon, la question soulevée par Meriton (les champs sont initialisés après que le super constructeur a été exécuté) est résolu. Merci a tous !



1
votes

Vous ne pouvez pas faire cela dans le constructeur car la super classe va être initialisée avant tout ce qui est dans la sous-classe. Ainsi, l'accès aux valeurs spécifiques à votre sous-classe échouera dans votre super constructeur.
Pensez à utiliser une méthode d'usine pour créer votre objet. Par exemple: XXX

Vous avez un problème dans cet échantillon particulier où MyClass ne peut pas être sous-classée, mais vous pourriez faire protéger le constructeur. Assurez-vous que votre classe de base possède un constructeur public / protégé également pour ce code. Il est juste censé illustrer que vous avez probablement besoin d'une initialisation de deux étapes pour ce que vous voulez faire.

Une autre solution potentielle que vous pouvez utiliser consiste à utiliser une classe d'usine qui crée toutes les variantes de cette classe abstraite et vous pourriez passer le champ dans le constructeur. Votre usine serait le seul à savoir sur le terrain et les utilisateurs de l'usine pourraient être inconsciemment.

EDIT: Même sans l'usine, vous pouvez rendre votre classe de base abstraite nécessite le champ dans le constructeur afin que toutes les sous-classes doivent transmettre une valeur à une valeur ajoutée lorsqu'il est instancié.


1 commentaires

Je ne veux pas ajouter d'arguments au constructeur, mais je vais utiliser une initialisation en deux étapes comme vous le suggérez, ainsi qu'un getter abstrait comme suggéré par Crozin. Merci !



14
votes

aussi, je ne veux pas donner la valeur du champ comme un argument de la constructeur. p>

Pourquoi pas? C'est la solution parfaite. Faire le constructeur protégé code> et n'offrer pas de constructeur par défaut et les implémentations de sous-classe sont obligées de fournir une valeur dans leurs constructeurs - qui peuvent être publiques et transmettre une valeur constante à la superclasse, ce qui rend le paramètre invisible pour les utilisateurs de les sous-classes. P>

public abstract class Tool{
    protected int id;
    protected Main main;
    protected Tool(int id, Main main)
    {
        this.id = id;
        this.main = main;
    }
}

public class Pencil{
    public static final int PENCIL_ID = 2;
    public Pencil(Main main)
    {
        super(PENCIL_ID, main);
    }
}


4 commentaires

Mes sous-classes sont en fait une sorte de modules utilisés par mon programme principal. Le constructeur doit donc être public et prendre exactement les arguments avec lesquels ils seront appelés (comme déclaré dans la superclasse). Donc, et je ne vois pas comment je pourrais utiliser votre solution dans ce cas.


Dans la classe de base, disposez d'un constructeur protégé qui prend le paramètre et, dans la sous-classe, a les constructeurs publics que vous souhaitez, passez simplement le paramètre souhaité sur le constructeur de Superclass: Super (my_Constant) +1 à la suggestion de Michael


En utilisant cette solution, est-ce que je pourrais forcer les sous-classes à mettre en œuvre le constructeur public "Public XXXX (principal principal)"?


Non, il n'y a aucun moyen de contrôler les constructeurs de sous-classe.



1
votes

Si la valeur est déterminée par le type de sous-classe, pourquoi avez-vous besoin d'un champ du tout? Vous pouvez avoir une méthode abstraite simple qui est mise en œuvre pour renvoyer une valeur différente pour chaque sous-classe.


0 commentaires

1
votes

En outre, je ne veux pas donner la valeur du champ comme un argument du constructeur. P>

Y a-t-il une solution pour le faire ou devrais-je simplement changer de conception? P>

Oui, je pense que vous devriez changer votre conception afin que la sous-classe passe la valeur au constructeur. Étant donné que la partie de sous-classe de votre objet n'est pas initialisée avant que après em> le constructeur de superclasse est retourné, il n'y a vraiment pas d'autre moyen propre de le faire. Bien sûr, cela fonctionnera: p> xxx pré>

... depuis la mise en œuvre de abstractfield () code> ne fonctionne pas sur l'état de l'objet. Cependant, vous ne pouvez pas garantir que les sous-classes ne penseront pas que c'est une bonne idée d'être un peu plus dynamique, et de laisser abstractfield () code> retourne une valeur non constante: p>

class Sub2 {
    private int value = 5; 
    protected int abstractField(){ return value; }
    public void setValue(int v){ value = v; }
}
class Sub3 {
    private final int value; 
    public Sub3(int v){ value = v; }
    protected int abstractField(){ return value; }
}


0 commentaires

2
votes

Pourquoi n'avez-vous d'utiliser le modèle de modèle?

public abstract class Template {

    private String field;

    public void Template() {
        field = init();
    }

    abstract String init();
}


0 commentaires

0
votes

Si je vous comprends correctement: vous voulez que le constructeur de la classe abstraite fasse quelque chose en fonction d'un champ de la classe abstraite, mais qui est défini (espérons-le) par la sous-classe?

Si je me suis trompé, vous pouvez arrêter de lire ...

Mais si je l'ai eu raison, vous essayez de faire quelque chose qui est impossible. Les champs d'une classe sont instanciés dans l'ordre lexical (et donc si vous déclarez des champs "ci-dessous" ou "après", le constructeur ne sera pas instancié avant que le constructeur ne soit appelé). En outre, la JVM traverse toute la superclasse avant de faire quelque chose avec la sous-classe (c'est pourquoi l'appel "Super ()" dans un constructeur de sous-classe doit être la première instruction du constructeur ... parce que c'est simplement "conseil" à le JVM sur la façon de gérer le constructeur de la superclasse).

Ainsi, une sous-classe commence à instancier uniquement après que la superclasse a été complètement instanciée (et que le constructeur de SuperClass est renvoyé).

Et c'est pourquoi vous ne pouvez pas avoir de champs abstraits: un champ abstrait n'existerait pas dans la classe abstraite (mais seulement dans la sous-classe) et est donc sérieusement (!) "Off Limits" à la superbe (abstraite) ... Parce que la JVM ne peut rien lier quelque chose de références au champ (car cela n'existe pas).

J'espère que cela aide.


0 commentaires