[Swift 3-4] override lazy var ?
Jérémy
Membre
Bonjour à tous !
J'ai fait des tests hier soir sur playground et visiblement on ne peut pas surcharger une propriété lazy. Exemple :
class A {
lazy foo: CGFloat = {
return CGFloat(2.0)
}()
init(){ }
}
class B: A {
// Ne compile pas
override lazy foo: CGFloat = {
return CGFloat(3.0)
}()
init(){ }
}
Pour contourner le problème j'ai pensé à quelque chose comme ça :
class A {
lazy foo: CGFloat = {
return self.getFoo()
}()
init(){ }
func getFoo()-> CGFloat {
return CGFloat(2.0)
}
}
class B: A {
init(){ }
override func getFoo()-> CGFloat {
return CGFloat(3.0)
}
}
Mais je ne trouve pas cette solution très élégante... -_-
Quelqu'un aurait il mieux à proposer ? Bien entendu le lazy n'a aucun intérêt dans le cas ci-dessus, ce qui m'intéresse c'est le principe de fonctionnement.
Je suis ouverte à toute proposition (décente, bien entendu). :P
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Ton code ne compile pas du tout. il manque le moat "var".
Quand même, en le corrigeant, t'as raison que l'on ne peux pas overrider les lazy vars.
C'est pareil avec les vars qui ne sont pas lazy mais que l'on veuille initialiser dans la déclaration :
Les lazy vars ne sont que les stored vars qui sont initialisées sur le premier appel ; du coup, même principe.
La solution : n'utilise, ni les lazy vars, ni les vars initialisées si tu prévois les overrider.
Pour le comportement que tu recherches, il faut utiliser les "computed" vars :
Ou, si tu prévois changer la valeur après l'initialisation, tu peux faire :
J'ai créer ce bout de code au moment où j'ai ouvert le post. J'avais rien pour tester si ça fonctionnait.
Ouai c'est bien chiant... ::)
J'avais bien pensé à ce cas là mais le souci c'est qu'à chaque appel il va recréer une instance de type CGFloat. Chaque appel de la property foo retournera un nouvel objet. J'ai juste ? Si c'est la cas ça ne me va pas...
Sinon la solution que j'ai proposée avec une surcharge de fonction, tu valides ?
Tu n'as pas encore dit si tu prévois changer la valeur après l'initialisation. La réponse à ta question dépend sur ce condition.
Dans le cas d'un CGFloat, on ne crée pas un objet ; CGFloat est un struct qui à tout modification sera copié du coup, si tu faisais :
... le let f contiendra a.foo jusqu'au moment ou tu le modifie ; là il sera copié. ça c'est la nature des structs.
Oui effectivement... :P
En fait non, elle doit garder la valeur de la première initialisation (comme un lazy) et de mon côté ce seront des classes qui seront retournées et non des structures.
S'il y a une possibilité que les objets dans les vars ne soit pas utilisé, tu avais raison de préférer les lazy vars.
Du coup, une lazy var qui appelle une méthode virtuelle sera la meilleur solution :
createBigThing() ne sera appeler qu'une fois, lors du premier appel au lazy var. Après ça, le lazy var gardera le même objet.
ça marche ! Merci beaucoup !
Vraiment pénible qu'on ne puisse pas overrider un lazy (du moins une propriété stockée). Je vois pas trop où serait le problème...
Les lazy vars sont, en substance, l'équivalent de qqch comme :
Mais, avec ça, on a l'erreur : Cannot use instance member 'createBigThing' within property initializer; property initializers run before 'self' is available
Du coup, c'est pour les raisons comme ça que l'on a inventé les lazy vars, qui sont, en substance, une var qui est initialisée par un closure, à la place d'une var qui ne peux pas être initialisée par une méthode.
En fait, on peut oublier le closure sur le lazy var en faisant :
... parce que, en utilisant "lazy", ça dit au compilateur que la var ne puisse pas être appelé avant que "self" soit disponible.
Oui c'est exactement ça. Pout être plus précis, qu'il n'est pas obligatoire que la propriété soit instanciée à l'issue de l'instanciation de la classe qui la contient. En d'autres termes, elle ne doit pas être forcément initialisé avant ou à l'issue de l'appel de la méthode init. D'où le fait (comme tu le dis) que l'on utilise généralement cette mécanique pour construire des propriétés stockées après que "self" ait été construit.
Tu vois pas où est le problème ? Bah c'est pourtant simple. Toi tu résonnes comme si ton lazy var était une méthode. Effectivement dans le cadre d'une fonction tu peux la surcharger dans les classes filles. Alors que dans ton cas, même si tu as une closure qui te donne l'impression d'écrire une méthode, ton lazy n'est rien d'autre qu'une propriété stockée où l'instanciation de la classe qui la contient peut précéder son initialisation.
ça te viendrait à l'esprit de surcharger une propriété protected ou public ( ) d'une classe en Java ?
Non, c'est toi qui l'a dit ; c'est toi qui a cité toi-même. Mais quand-même ::)
Je n'ai jamais parlé d'un lazy var comme il était une méthode ; il est une propriété stockée, initialisée par une méthode (qui est une type de closure), et on peut overrider la méthode d'initialisation comme toutes autres méthodes. Mais on ne peut pas overrider le var lui-même.
Je ne comprends pas vraiment ce que tu veux dire. Un lazy var a été conçu exprès pour qu'il ne faille l'instancier qu'au premier appel. C'est l'équivalent de ce que l'on faisait auparavant :
Je ne touche jamais Java ; j'en suis allergique :-*
Oui je sais, j'avais trouvé la réponse et je me répondais à moi même pour ceux qui seraient intéressés d'avoir une explication.
Normal, c'est super poussiéreux comme truc. Même un Dyson n'y pourra rien. Quand tu passes de Swift à Java tu as la sensation de faire un bon dans le moyen âge.