À l'origine, je voulais créer une classe qui peut annuler l'instanciation dans le constructeur, mais selon ce lien , je devrais plutôt utiliser une classe Factory. Mais maintenant, je veux empêcher quiconque sauf la classe factory de créer un objet de la classe "Inner" tout en donnant accès aux méthodes de la classe interne à tout le monde.
J'ai déjà essayé cette réponse .
val date1 = Date(1547600000) val date2 = Date(1547600600) val inner = InnerFactory.createInnerObject(date1, date2) //should return an instance val invalidInner = InnerFactory.createInnerObject(date2, date1) //should not return an instance because the "endDate" is before "startDate" val difference = inner?.getTimeDifference()
Je l'utiliserais comme suit:
import java.util.Date
object InnerFactory {
class Inner private constructor(startDate: Date? = null, endDate: Date? = null) {
fun getTimeDifference(): Long? {
//calculates time difference but doesn't matter to this example
}
}
fun createInnerObject(startDate: Date? = null, endDate: Date? = null): Inner? {
if (startDate != null && endDate != null && !endDate.after(startDate)) {
return null
}
return Inner(startDate, endDate)
}
}
3 Réponses :
Malheureusement, les membres privés des classes internes de Kotlin ne sont pas accessibles depuis l'instance externe:
privatesignifie visible à l'intérieur de cette classe uniquement
Référence Kotlin / Modificateurs de visibilité
Cependant, Java n'est pas aussi restrictif avec ses modificateurs de visibilité:
l'accès est autorisé si et seulement s'il se produit dans le corps du type de niveau supérieur ( §7.6 ) qui renferme la déclaration du membre ou du constructeur.
Spécification du langage Java / §6 Noms / §6.6 Contrôle d'accès / §6.6.1 Détermination de l'accessibilité < / p>
C'est l'un des seuls cas (ennuyeux) que j'ai trouvés où les règles de Kotlin rendent impossible un modèle Java commun.
La seule solution de contournement ( si vous souhaitez conserver votre structure actuelle) serait de réécrire cette classe en Java, ou d'exposer ce constructeur avec une visibilité moins restrictive (par exemple internal code>.)
Il y avait un discussion à ce sujet sur les forums Kotlin - il semble que ce soit une limitation JVM, et que cela ne fonctionne qu'en Java car le compilateur génère des accesseurs synthétiques appropriés. p >
D'ACCORD. Merci quand même.
Ce que vous pourriez faire:
interface Inner avec toutes les fonctions nécessaires qui devraient être exposées privées et implémenter cette interface Sample:
// the following all work as it deals with the interface val inner = InnerFactory.createInnerObject(date1, date2) //should return an instance val invalidInner = InnerFactory.createInnerObject(date2, date1) //should not return an instance because the "endDate" is before "startDate" val difference = inner?.getTimeDifference() // the following will not compile: InnerImpl()
Vous ne pouvez plus accéder à InnerImpl depuis l'extérieur, mais vous disposez toujours de toutes les fonctions nécessaires :
object InnerFactory {
interface Inner {
fun getTimeDifference(): Long?
}
private class InnerImpl(startDate: Date? = null, endDate: Date? = null) : Inner {
override fun getTimeDifference(): Long? = TODO("some implementation")
}
fun createInnerObject(startDate: Date? = null, endDate: Date? = null): Inner? {
if (startDate != null && endDate != null && !endDate.after(startDate)) {
return null
}
return InnerImpl(startDate, endDate) // InnerImpl accessible from here but not from outside of InnerFactory...
}
}
bonne idée! +1 le seul inconvénient est que vous devez d'abord définir les propriétés / fonctions dans l'interface, puis les remplacer dans InnerImpl qui est un peu redondant.
oui, c'est vrai ... mais je pense qu'il est courant d'utiliser une interface pour de telles constructions ...
Vous pouvez rendre le constructeur protected . De cette façon, vous ne l'exposez qu'aux sous-classes, dans ce cas PrivateClass . Ensuite, vous allez créer une instance de PrivateClass ou null mais la renvoyer sous la forme InnerClass?.
object InnerFactory {
fun createInnerObject(startDate: Date? = null, endDate: Date? = null): Inner? {
// return null here if conditions are not met
return PrivateClass(startDate, endDate)
}
open class Inner protected constructor(val startDate: Date?, val endDate: Date?) {
fun getTimeDifference(): Long? { /* ... */ }
}
private class PrivateClass(startDate: Date?, endDate: Date?): Inner(startDate, endDate)
}
Notez que vous ne masquez pas ce constructeur de Inner alors ... vous pouvez toujours appeler quelque chose comme val myFakedInner = object: Inner (null, null) {} qui sera alors construire un objet Inner valide qui réutilise la fonctionnalité que vous vouliez masquer en premier lieu (si j'ai bien compris cette exigence) ... néanmoins en fonction du cas d'utilisation (et si vous détenez ce val s est ok) c'est aussi une variante valide ... vous pouvez cependant définir les val s sur private au moins ...
@Roland, vous pouvez hériter d'Inner. J'ai même dit que dans ma réponse "De cette façon, vous ne l'exposez qu'aux sous-classes ...", mais vous ne pouvez pas l'instancier directement. C'est la faiblesse de mon approche. :RÉ
vrai. en raison de "dans ce cas PrivateClass " Je pensais juste que j'allais en parler pour que ce ne soit pas mal interprété.
@Roland bien sûr, chose :)
Juste une note: Kotlin fait la différence entre les classes "internes" et "imbriquées". Votre classe
Innerest en fait une classe imbriquée. Une classe interne réelle est définie avec le mot-cléinner. La différence est qu'une classe interne porte une référence à un objet de la classe externe. Vous pouvez en savoir plus ici: kotlinlang.org/docs/reference/nested-classes. html