Comment créer une variable qui ne peut être définie qu'une fois, mais n'est pas définitive en Java
Je veux une classe qui me permet d'en créer des instances de avec une variable de la fonction unset (le id
), puis initialiser cette variable plus tard, et il immuable après l'initialisation. Effectivement, je voudrais un final
variable que je peux l'initialiser à l'extérieur du constructeur.
Actuellement, je suis en improvisant avec un setter qui déclenche une Exception
comme suit:
public class Example {
private long id = 0;
//Constructors and other variables and methods deleted for clarity
public long getId() {
return id;
}
public void setId(long id) throws Exception {
if ( this.id == 0 ) {
this.id = id;
} else {
throw new Exception("Can't change id once set");
}
}
}
Est-ce une bonne façon d'aller sur ce que je suis en train de faire? Je me sens comme je devrais être capable de mettre quelque chose comme immuable une fois initialisé, ou qu'il y est un modèle que je peux utiliser pour le rendre plus élégant.
- Est-il une BONNE raison pour ne pas le mettre dans le constructeur?
- que faire si l'OP doit avoir un constructeur par défaut ainsi?
- Est-ce quelque chose qui pourrait se produire et doit être traité au cours de l'exécution du programme ou voulez-vous empêcher le programmeur de la mise plus d'une fois?
- ce n'est pas le programmeur vous avez besoin de s'inquiéter.
- puis je pense que ça devrait être ok pour être null (et pas en dernier) - une variable qui peut être définie qu'une fois se sent comme une pure violation du "principe de moindre surprise"
- J'ai demandé parce que si ce cas ne devrait jamais se produire, j'utiliserais une affirmation plutôt qu'une exception.
- et si le contrat de la classe stipule que tous les champs sont initialisés à des valeurs non nulles qu'est-ce que la suggestion? Je comprends que c'est un cas extrême, mais le contrat doit être respecté.
- comment pourrait-il jamais être évité 🙂 si les champs doivent être non null, comment est définie une fois que la variable de la solution?
- vous n'null case en regard de la valeur. Faire du design par contrat, bien que fastidieux, est d'une grande utilité dans ces scénarios. Si vous assurer que tous les mutateurs avoir à passer par une vérification sur l'objet que vous pouvez vérifier que votre objet a été mis à jour correctement.
- Mais ils ne sont pas encore initialisé à la non null.
- Quel est le problème avec cette façon de faire? Je l'aurais fait de la même façon: un setter qui lève une exception si vous essayez de régler une deuxième fois.
- Je suis d'accord même si je pense toujours que c'est un mauvais "modèle".
- Je ne peux pas mettre dans le constructeur, car je ne connais pas l'id de l'époque. L'ID est retourné à partir d'un serveur distant à une date ultérieure. Après l'écriture de cette question, j'ai décidé de re-conception de mes interactions afin que le serveur crée l'objet lui-même, mais je suis toujours intéressé à la meilleure approche ici.
- Je pense que l'utilisation d'affirmer, c'est plus élégant que ce que j'ai fait ci-dessus, mais ça ne signifie pas que l'exception ne sera pas explicitement être jeté, donc je vais probablement oublier de le rattraper.
- Quelle est la portée? Avez-vous besoin d'exposer la méthode de définition d'une manière ou d'un être isolé dans la même classe/méthode? Si son petit périmètre (classe, méthode, usine, package), vous pourriez être en mesure d'avoir une interface sans le setter qui vous exposer à l'extérieur et à créer de la mise en œuvre effective à l'intérieur de votre "champ d'application"....
- J'aime l'idée de l'interface, très soigné. Pense à ce sujet.
- cambecc dit ci-dessous "les Assertions sont désactivés par défaut--ils sont activés uniquement avec l'option-ea drapeau sur la JVM de la ligne de commande. Si le comportement de vos changements de classe en fonction de cet indicateur. Sans assertions activé (encore une fois, c'est la valeur par défaut), vous pouvez définir l'id autant de fois que vous le souhaitez." Peut-être les assertions ne sont pas si bonnes...
Vous devez vous connecter pour publier un commentaire.
Permettez-moi de vous suggérer un peu plus élégant décision.
La première variante (sans jeter une exception):
Deuxième variante (avec la levée d'une exception):
null
valeur est quelque chose d'autre, qu'un type numérique, qui est tenue par votre variable. Il vous permet d'attribuer0
(zéro) de la valeur, et de lever une exception dans le cas où cette valeur est assignée à nouveau (dans le cas où la première valeur de la pièce d'identité sera== 0
, votre test permettra d'avoir de nouvelles missions, mais il faut ne le permettent pas).La "définie une seule fois" exigence se sent un peu arbitraire. Je suis assez sûr de ce que vous recherchez est une classe que les transitions de façon permanente à partir non initialisé à l'état initialisé. Après tout, il peut être pratique de définir un identifiant d'objet plus d'une fois (via la réutilisation de code ou quoi que ce soit), tant que l'id n'est pas autorisé à changer après que l'objet est "construit".
Un assez raisonnable motif est de garder une trace de ce "construit" de l'état dans un champ distinct:
Utilisation:
Avec ce modèle, vous construire l'objet, de définir ses valeurs (autant de fois qu'il est pratique), et ensuite appeler
build()
à "immutify" il.Il y a plusieurs avantages à ce motif au-dessus de votre approche initiale:
0
est tout aussi valable un id comme tout autrelong
valeur.build()
est appelé, ils travaillent. Aprèsbuild()
est appelé, ils jettent, peu importe quelles sont les valeurs que vous transmettez. (Notez l'utilisation incontrôlée des exceptions pour des raisons de commodité).final
, sinon un développeur pourrait étendre votre classe et de remplacer les setters.Mais cette approche a un gros inconvénient: les développeurs à l'aide de cette classe ne peut pas savoir, au moment de la compilation, si un objet particulier a été initialisé ou non. Bien sûr, vous pourriez ajouter un
isBuilt()
méthode afin que les développeurs peuvent vérifier, au moment de l'exécution, si l'objet est initialisé, mais il serait tellement plus pratique de connaître cette information au moment de la compilation. Pour cela, vous pouvez utiliser le générateur de modèle:Utilisation:
C'est beaucoup mieux pour plusieurs raisons:
final
champs, de sorte que le compilateur et les développeurs de connaître ces valeurs ne peuvent pas être modifiés.Oui, c'est un peu plus compliqué à maintenir, mais à mon humble avis, les avantages l'emportent sur les coûts.
build()
pour arrêter toute autre modification de l'objet.Example e = new Example(12345L)
. La distinction provient de ce que les types sont de vous dire. Si vous avez une instance deExample.Builder
, vous le savez, tous les champs sont modifiables à tout moment (y compris l'id, lorsque vous avez fini par le mettre). Mais une fois que vous avez une instance deExample
, vous savez que l'objet a été entièrement initialisé et de ne pas (ne peuvent pas) changer dans le futur. C'est une très puissante garantie.Vous pouvez simplement ajouter un indicateur booléen, et dans votre setId(), régler/vérifier la valeur de type boolean. Si j'ai bien compris le droit de la question, nous n'avons pas besoin de toute la complexité de la structure/modèle ici. Comment à ce sujet:
de cette façon, si vous
setId(0l);
il pense que l'ID est de trop. Vous pouvez le modifier si ce n'est pas bon pour votre entreprise logique d'exigence.pas édité dans un IDE, désolé pour la faute de frappe/format de problème, si il y avait...
Google Goyave bibliothèque (que je recommande très fortement) est livré avec une classe qui permet de résoudre ce problème très bien:
SettableFuture
. Cela fournit à la fois de la sémantique que vous vous posez au sujet, mais aussi beaucoup plus:setException
méthode);ListenableFuture
de l'interface).Future
de la famille de types en général utilisé pour la synchronisation entre les threads dans les programmes multithread, doncSettableFuture
joue très bien avec ces.Java 8 a aussi sa propre version de ce:
CompletableFuture
.Voici la solution que j'ai trouvé basé sur un mélange de certaines des réponses et des commentaires ci-dessus, en particulier celui de @KatjaChristiansen sur l'utilisation de l'affirmer.
À la fin de la journée, je soupçonne que mon besoin c'est un signe de mauvaise décisions de conception d'ailleurs, et je devrais plutôt trouver un moyen de la création de l'objet seulement quand je sais que le Id et le réglage de l'id de finale. De cette façon, plusieurs erreurs peuvent être détectées au moment de la compilation.
-ea
drapeau sur la JVM de la ligne de commande. Si le comportement de vos changements de classe en fonction de cet indicateur. Sans assertions activé (encore une fois, c'est la valeur par défaut), vous pouvez définir l'id autant de fois que vous le souhaitez.J'ai cette classe, semblable à Du JDK
AtomicReference
, et je l'utilise principalement pour le code de legs:Je a seule responsabilité et vérifier les deux
get
etset
appels, donc il échoue dès le début, quand le code client en abuser.Ici de deux manières; la première est essentiellement la même que quelques autres, mentionné dans d'autres réponses, mais il est ici pour en contraste avec les secondes. La première manière, une Fois est d'avoir une valeur qui peut être définie qu'une seule fois par l'application que dans l'incubateur. Ma mise en œuvre requiert des valeurs non nulles, mais si vous voulez être en mesure de mettre à null, alors vous devez mettre en œuvre un "isSet' indicateur booléen comme suggéré dans d'autres réponses.
La deuxième façon, Paresseux, est de fournir une fonction qui paresseusement fournit la valeur une fois que le premier temps de la lecture est appelée.
Vous pouvez donc utiliser ces comme suit;
essayer d'avoir un int checker comme
//u pouvez essayer ceci:
HTML:
Marquage d'un domaine privé et ne pas s'exposer à un
setter
devrait être suffisant:si cela est insuffisant et que vous voulez quelqu'un pour être en mesure de le modifier X fois vous pouvez le faire:
Cette approche s'apparente à la conception par contrat, par lequel vous le valider l'état de l'objet après un mutateur (quelque chose qui change l'état de l'objet) est invoquée.
final
sans l'aide du mot-clé.Je pense que le pattern singleton peut être quelque chose que vous devriez regarder dans. Google un peu autour de lui pour vérifier si ce modèle répond à vos objectifs de conception.
Ci-dessous quelques sudo code sur comment faire un singleton en Java en utilisant le protocole enum. Je pense que c'est basé sur Joshua Bloch de conception décrites dans l'efficacité de Java, de toute façon c'est un livre que la peine de ramasser si vous ne l'avez pas encore.
Utilisation: