I'm trying to create a class with some fields of a...
# getting-started
r
I'm trying to create a class with some fields of a type that should only be constructed when those fields are initialised, but I can't work out how to make the type system do it. Code in thread.
This does not compile, because the
FeatureFlag
constructor is private:
Copy code
class FeatureFlags(
  private val featureFlagsClient: FeatureFlagsClient,
) {

  val myNewFeature = FeatureFlag(key = "my-new-feature", defaultValue = false)

  inner class FeatureFlag private constructor(val key: String, val defaultValue: Boolean) {
    fun isEnabled(): Boolean = featureFlagsClient.isEnabled(this)
  }
}

interface FeatureFlagsClient {
  fun isEnabled(flag: FeatureFlags.FeatureFlag): Boolean
}
However, if I make it
internal
then it is possible to construct a
FeatureFlag
from outside the body of `FeatureFlags`:
Copy code
FeatureFlags(featureFlagsClient).FeatureFlag("escaped-feature-flag", true)
Is there a way to restrict the scope of the constructor to the file?
OK, worked it out. Extract an interface and then the inner class can be private
Copy code
package mocklab.system.featureflags

interface FeatureFlag {
  fun isEnabled(): Boolean
}

class FeatureFlags(
  private val featureFlagsClient: FeatureFlagsClient,
) {

  val myNewFeature: FeatureFlag = ClientBackedFeatureFlag(key = "my-new-feature", defaultValue = false)

  private inner class ClientBackedFeatureFlag(
    override val key: String,
    override val defaultValue: Boolean,
  ) : FeatureFlag, FeatureFlagDefinition {
    override fun isEnabled(): Boolean = featureFlagsClient.isEnabled(this)
  }
}

interface FeatureFlagDefinition {
  val key: String
  val defaultValue: Boolean
}

interface FeatureFlagsClient {
  fun isEnabled(flag: FeatureFlagDefinition): Boolean
}
👍 1
s
I think I would still try to get rid of the circular dependency though… if you change it to
Copy code
interface FeatureFlagsClient {
  fun isEnabled(key: String): Boolean?
}
it'll be much easier to test 😄
At the moment the feature flags depend on the client and the client depends on the feature flags, it's quite exciting
Inner classes allow all kinds of fun like that
r
I pulled out the
FeatureFlagDefinition
interface to get round that - I don't think there's a circular dep any more
s
Oh I see, I missed that change
r
Oh, maybe there is...
s
No I think you got it
At least the bit I was worried about
r
I think it's OK now - yes, the circular dep in the original was bothering me too!