Refer to super in default values of constructor parameters when implementing an interface - interface

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?

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() {
}
}

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() {
// ...
}

Since I now figured out how to do it with the vars, 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 }
}

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.

Related

reassignment to val while initializing in primary constructor

class Person(){
val name : String
def this(n : String) {
this()
this.name = n
}
}
compile time error : reassignment to val
i am a newbie to scala and so far i learned how to use primary constructor and case classes for initialization of data members. I am just wandering, if there is a way to initialize val data member inside this. Initialization of var data member works fine below :-
class Person(){
var name : String = _
def this(n : String) {
this()
this.name = n
}
}
You just can't assign to a val after initialization. In Scala the body of the class IS the constructor, you can see examples here.
In general, you just define all variables in the primary constructor itself as "class parameters": class Person(val name: String) if you need to receive the name for initialization or class Person() { val name = 'Joe' } if it is fixed.
This can be quite surprising coming from Java, as you are used to have constructors that produce the values and build the object directly. For such a case, the best solution is to use apply methods on a companion object:
class Person(val name: String)
object Person() {
def apply(db: SomeDBConnectionWrapper, id: Int) = new Person(db.fetchName(id))
}
That allows you to call Person(db, 3) to get a new person with custom initialization, but the constructor itself still receives all it needs to construct a new instance where all values are only assigned once.

Override attribute's methods in scala

In scala it's quite easy to override the default accessors and mutators; this makes me feel much more comfortable with public attributes because it allows me to modify access behavior at a later time without changing my class's signature. Here's how that's done:
class Topic {
var tags = "default";
}
becomes:
class Topic{
private var _tags = "default";
def tags = {
_tags
}
def tags_ =(s:String){
_tags = s
}
}
However, this doesn't address a major reason for creating private fields; it doesn't allow for the modification of a component element's behavior. For example, with a Growable, I may want to do something special if I add a new element or clear my list. One way to approach this is to create a class that implements Growable, for example:
class Topic {
private var _tags:Growable[String] = new MyMutableList[String]()
def tags = {
_tags
}
def tags_=(Growable[String]){
_tags = m
}
}
class MyMutableList[T] extends MutableList[T](){
override def +=(t:T) {
println("adding: " + t.toString())
super.+=(t)
}
override def clear() {
println("clearing")
super.clear()
}
}
However, this doesn't fix the problem completely because 'tags' can still be set equal to any Growable[String]. This would change the clear() method's behavior to the behavior of whatever type was provided.
Is there anyway to override a method of an attribute in the same way that the attribute's accessor/mutator can be overridden? This doesn't compile, but it expresses what I'm trying to do:
class Topic {
private var _tags:Growable[String] = new MutableList[String]();
def tags = {
_tags
}
def tags_=(m:Growable[String]){
_tags = m
}
def tags_.+=(s:String) {
println("adding: " + t.toString())
_tags += s
}
def tags_.clear(){
println("clearing")
_tags.clear()
}
}
You cannot override the methods of an attribute in a class.
You could limit the type of your tags attribute, to make it
impossible to set the tags attribute to a different
Growable[String}.
You could also omit the mutator method and add a
addTag(tag: String): Unit and a cleanTags : Unit method.

Scala inheritance default parameter in parent class

I have an abstract class with a default value for its parameter.
I don't want to have to reuse the default value in the constructor of all the possible implementations.
abstract class Place(val place: String = "World")
class Message(val message: String = "Hello", p: String) extends Place(p) {
override def toString = s"$message $place"
}
What I want to get
new Message("Hi", "Universe") = "Hi Universe" // Ok
new Message("Hi") = "Hi World" // Doesn't work, second parameter is required
new Message() = "Hello World" // Doesn't work, second parameter is required
I considered using an auxiliary constructor omitting the second parameter, but it doesn't help since you can't call super constructors outside of the main constructor.
I want to know how to do it, or why it is not possible. I'm not looking for a workaround, like not using inheritance.
I'm afraid that is not possible. Quite simply, you ARE passing a value to Place constructor, so it wont use the default, whatever its value might be. If you don't mind having a var instead of a val, here is a variant that works for your 3 cases:
abstract class Place(var place: String = "World")
class Message(val message: String = "Hello") extends Place()
{
def this(message: String, place: String) = {
this(message)
this.place = place
}
override def toString = s"$message $place"
}
Constructors in Scala are a little of a mess IMHO. Sometimes a better answer is just to use factory apply() methods on a companion object, which are more flexible.
You can reuse the default value in a more elegant way:
object Place {
val defaultPlace = "World"
}
abstract class Place(val place: String = Place.defaultPlace)
class Message(val message: String = "Hello", p: String = Place.defaultPlace) extends Place(p) {
override def toString = s"$message $place"
}

How to create private fields that my functions will return

My class currently looks like:
class WebConfig(config: Config) {
def this() {
this(ConfigFactor.load())
}
def dbPort = config.getInt("mysql.port")
}
I don't like that when I call dbPort, it has to call and then cast the config each and every time.
So I want to create private fields and set them in the constructor, so then calling dbPort will simply return what a private field has.
How can I do this?
I tried creating a private var but I am getting this error:
class WebConfig(config: Config) {
private var _dbPort: Int
def this() {
this(ConfigFactor.load())
_dbPort = config.getInt("mysql.port")
}
def dbPort: Int = _dbPort
}
Error:
abstract member may not have private modifier
You are getting that error because your private variable is not initially assigned a value, so is treated as abstract, and abstract variables can't be private because they can not be accessed by the sub classes that are required to implement them.
To fix this you can assign a placeholder value to your variable like this:
private var _dbPort: Int = _
That being said, I think vptheron and kululimpa's suggestions are the way to go.
Can't you just write this:
class WebConfig(config: Config) {
def this() {
this(ConfigFactor.load())
}
val dbPort = config.getInt("mysql.port")
}
It will only read the config parameter one time.

Scala: to put constraints on the fields of a class

In Scala how to put constraints on the fields of a class?
In a package I have the domain of my model, in another package I have dsl to instantiate my model.
The basic form of the model is this:
abstract class Element {
var name: String
var description: String
var types : Set[Type]
}
class SAComponent (var name :String,
var description : String,
var properties : Set[Property] = Set(),
var types : Set[Type] = Set(),
) extends Component
Element is the root of my model.
I want to put constraints on the fields of Element, so that each class that inherits name and description and types of Element respects these constraints.
In other words I need to define the I get for these fields. Right?
How should I do?
I tried that, but the constraints are not respected:
abstract class Element {
def name: String
def name_= (value: String): Unit = {if (isBadValue(value)throw new IllegalArgumentException
name = value
}
var description : String,
var types : Set[Type] = Set }
class Component (override var name : String, var description: String) extends Element
The problem is that some fields that must respect the constraints,
in the constructor of the concrete classes must be initialized to a null value. So the "require" for me is not a good solution.
Thank you all.
Checking at initialization does not work for you because you want stateful objects, which can be avoided by using case classes. Instead of mutating the state of an object, you may want to create new objects using copy(field=value), which is automatically generated for case classes.
If you still want to go with stateful objects, I guess you want something like
abstract class Element {
private var _name: String = null
def name_= (value: String) {
require(!isBadValue(value),"Bad Value")
_name = value
}
def name = _name
def isBadValue(value: String): Boolean
}
class Component (initialName : String) extends Element {
name = initialName
def isBadValue(value: String) = value=="name"
}
val c = new Component(null) // works
c.name = "name" // exception
Another thing to point out: the setter generated by override var name in your code overrides your name_= method, which you may already know.
Abstract val:
trait Init {
val name: String
require(!isBadName(name))
def isBadName(name: String) = true
}
Creation:
new { val name = "init" } with Init