Reputation: 3376
Assume I have an interface that provides many immutable properties with default getters, as such
interface Repository {
val name : String
val pattern : String
get() = "foo/bar/baz"
var url : String
get() = "http://mycompanyrepo.com/$name"
fun add() {
TODO("do something interesting here")
}
}
Now, there are several concrete implementations that just use most of the defaults. However, I also want to provide a ConfigurableRepository
that allows for more flexibility in other projects that want to configure this at runtime, based on other user parameters.
How can I create a primary constructor that has optional parameters?
I was hoping something along the lines of:
class ConfigurableRepo(var name, var pattern, var url) {
...
}
Edited for clarification
The intent here is to use primary constructor behavior, where the named parameters are optional. More specifically, name
, pattern
, and url
are all optional to anyone calling the constructor, and it will default to the interface's getters. But, I'm obviously trying to force the Kotlin interface
to fit this model and it is probably a noob Kotlin user mistake, as I ultimately have no access to super here.
From the class docs
In fact, for declaring properties and initializing them from the primary constructor, Kotlin has a concise syntax:
class Person(val firstName: String, val lastName: String, var age: Int) {
// ...
}
I seem to be getting errors that this hides members of the supertype and requires the override
modifier. If I try to override the supertype and then provide a default, it complains that supertypes aren't accessible in this context.
Can this only be done in a secondary constructor with mutable values?
I'm also open to suggestions/feedback on better ways to provide an interface that supports configuration over convention. The most important aspect being that the interface always provide defaults for a convention. Or, more simply put:
Is there a better way?
Upvotes: 2
Views: 1121
Reputation: 11541
Since I now figured out how to do it with the var
s, here an answer for the exact interface from your question.
class ConfigurableRepo(
private var _name: String,
private var _pattern: String,
private var _url: String) : Repository
{
override val name get () = _name
override val pattern get () = _pattern
override var url
get () = _url
set (u: String) { _url = u }
}
Upvotes: 1
Reputation: 11541
If your intention is to have default values for the parameters of the default constructor like this (pseudocode, which does not compile):
class ConfigurableRepo(
override var name,
override var pattern = super.pattern,
override var url = super.url
) {
...
}
Then the way to go would be the "null as default pattern" which one can always use when the desired default value can't be expressed in the method declaration. In your case, it could work out like this:
class ConfigurableRepo (
override val name: String,
_url: String? = null,
_pattern: String? = null
): Repository {
override val pattern = _pattern ?: super.pattern
override var url = _url ?: super.url
// ... more code ...
}
As you can see, the trick works with val
as well as var
properties and fields. Property name
which does not have a default value in the interface is implemented with straight-forward field in this class.
Upvotes: 0
Reputation: 29844
Use an abstract class like this:
abstract class Repository {
open val name = "default"
open val pattern = "default"
open val url = "default"
}
// example: only override name
class RepoWithDifferentName(
override val name: String
): Repository() {
// ...
}
Upvotes: 1
Reputation: 5287
Please have a look at https://kotlinlang.org/docs/reference/visibility-modifiers.html
Especially the 2nd paragraph around Classes and interfaces.
From these examples, a suggestion would be, if it's possible in your case, to use a base class instead of an interface. This would look something like this :
open class DefaultRepo {
protected open val name = "foo/bar/baz"
protected open val pattern = "foo/bar/baz"
protected open val url = "http://mycompanyrepo.com/$name"
open fun add() {
TODO("do something interesting here")
}
}
class RenamedRepo(newName: String): DefaultRepo() {
override val name = newName
}
class ConfigurableRepo(n: String, p: String, u: String): DefaultRepo() {
override val name = n
override val pattern = p
override val url = u
override fun add() {
}
}
Upvotes: 1