4

This question has been answered in Stack overflow before but since I am new to Kotlin I didn't understand that. It would be very helpful If someone can tell me what is wrong in the below code (In simple language) and how I can fix that. I am getting a warning from IntelliJ IDEA under 'name' property in Country class that I am trying to access non final property name in constructor. What is wrong with that?

open class Country (n : String? = null)
{
    open var name : String? = n

    init {
        if (n==null)
        {
            this.name = "Unknown Country"
        }
        else
        {
            this.name = n
        }
    }

    fun printCountryName()
    {
        println("Country Name : ${this.name}")
    }

}

class India constructor(n: String? = null, p: Int, g: Double ): Country(n)
{
    override var name : String? = "India"

}
Sujit Singh
  • 51
  • 1
  • 5
  • `It's okay if you don't understand another similar question - just clearly point out what you don't understand.` – no ai please May 22 '21 at 03:36
  • 2
    I assume the reason why you should not do this is the same reason for [What's wrong with overridable method calls in constructors?](https://stackoverflow.com/questions/3404301/whats-wrong-with-overridable-method-calls-in-constructors) in Java. – Slaw May 22 '21 at 03:49
  • Are you sure you want that `var` to be `open`, while we're at it? It seems unnecessary to allow subclasses to *override* the property. Perhaps you meant for subclasses to *assign to* the existing property? If it's not `open`, then you don't have this problem. – Silvio Mayolo May 22 '21 at 04:38
  • Thanks Someone_who_likes_SE , Slaw and Silvio. I would like to elaborate in detail what I want to achieve. Suppose I have only created a country class in my kotlin file with the code mentioned above and everything is working fine. Next day my friend told me that he wants to create India class and wants to extend my class. His requirement is that country name will always be India unlike mine wherein user passes the name parameter and then I perform checks in initializer block and based on that set country name as either "Unknown" or something based on user input. – Sujit Singh May 22 '21 at 05:32
  • Comment (2/2) : I put open keyword in front of (var name : String? = n) in my Country class because my friend will override that to "India" in his India class. How can I ensure that I dont have to change my Country class code and also ensure that my friend can inherit all the properties and functions of my class and provide his own implementation as per his choice in his class. – Sujit Singh May 22 '21 at 05:43
  • 1
    Why not get rid of the `init` block and just use `val name = n ?: "Unknown Country"`? Then it's up to the subclass to pass the appropriate name to the constructor. For example, your friend would do `class India : Country("India")`. Of course, making a subclass for each country is not a viable approach (it doesn't scale well). If you need an "India country" then you should simply do `foo = Country("India")`. – Slaw May 22 '21 at 08:11

1 Answers1

1

You need to remember that properties aren't just fields. They're fields + getters/setters. By declaring name as open you let your subclasses change the implementation of the setName(). Then, any subclass can do something like this:

class MyCountry : Country("foo") {
    override var name: String?
        get() = null
        set(value) {}
}

This way the constructor of Country won't be able to set the name.

open is for changing the implementation, not for changing a stored value. If you need some default value then just set it as normal:

class India constructor(p: Int, g: Double ): Country("India")

To fix your problem you need to just remove open from name, because from your example it doesn't seem you really need it.

Update

Also, I see potential bugs/mistakes in your code:

  1. If you check Country name for nullability when constructing and replace null with "Unknown Country" then why name proeperty is String? and not String? It seems you don't want to have nulls there, but still allows them.
  2. I guess it doesn't make sense to change the name of existing country, so I believe you could use val instead of var.
broot
  • 21,588
  • 3
  • 30
  • 35
  • sorry for the trouble but can you look at this [question](https://stackoverflow.com/questions/72028886/access-kotlin-constructor-in-java-class) – lazydevpro Apr 30 '22 at 09:47