22

I am new to kotlin. i am going through Nested class and inner class concepts in kotlin. Below is a example of nested class.

 fun main(args: Array<String>) {
   val demo = Outer.Nested().foo() // calling nested class method
   print(demo)
}
class Outer {
   class Nested {
      fun foo() = "Hello world"
   }
}

which gives output as

Hello world

here is the example of inner class.

fun main(args: Array<String>) {
   val demo = Outer().Nested().foo() // calling inner class method
   print(demo)
}
class Outer {
   private val welcomeMessage: String = "Hello world"
   inner class Nested {
      fun foo() = welcomeMessage
   }
}

which gives the outputs as

Hello world

my question is when nested class can perform the same operation as that of inner class . what is the purpose of inner class ?why we need inner class in real time?

Gaju Kollur
  • 2,046
  • 5
  • 23
  • 47
  • 10
    A nested class cannot do the same thing as an inner class. An inner class has an implicit reference to its outer class *instance*. A nested class doesn't. Remove the `inner` keyword and you'll have a compilation error. – JB Nizet Dec 21 '19 at 12:26
  • 8
    The `inner` is like leaving out the `static` in java – Minn Dec 21 '19 at 12:29
  • @JBNizet Answer with a simple examples will be more helpful sir – Gaju Kollur Dec 21 '19 at 12:32
  • 8
    You have posted the example yourself. Look at the code of your inner class: it accesses the outer class welcomeMessage property. A nested class can't do that. – JB Nizet Dec 21 '19 at 12:35

2 Answers2

23

Nested class itself is a rather straightforward idea of one concept being logically dependent on another, wider concept. You would like to declare a new nested class whenever you find yourself describing a building block of such a wider concept you do not expect to ever show up in your business logic on its own out of thin air. Therefore, an instance of such a class is accessed via the definition of the class it is nested in.

Take for an example the following structure:

class Parrot(val name: String): Animal {

    //vals and vars useful for a parrot

    class HealthyConditions {
        val featherColour: String // Does not really apply to Dogs, Cats, Fish, etc.
        val cageSize: Size // Does not really apply to Dogs, Cats, Fish, etc.
        val weightInGrammes: Int
        val dailySleepInHours: Int

        fun isUnderweight(currentWeightInGrammes: Int): Boolean {
            return currentWeightInGrammes < weightInGrammes
        }
    }
}

Now, the concept of healthy living conditions is common and important for any Animal this system may describe, but from one species to another there are multiple completely different factors to take into consideration. Trying to prepare a single common HealthyConditions<Animal> class for all of them at once will likely lead to unreadable and hard to maintain code, full of ifs and elses. Therefore, it may make more sense for the developer to define concise, tidy nested classes for each Animal on its own. Later on, these will be accessed through Parrot.HealthyConditions(), Cat.HealthyConditions(), etc. instead of HealthyConditions.getForSpecies(animal).


Inner class is a Kotlin concept built on top of the idea of a nested class. While some details are very specific for a given concept and therefore are being described as nested classes, what if those details vary even further depending on the wider concept's instance? Then accessing it using the class definition may not be all it takes to operate properly. Inner classes are therefore accessed through the call to an instance of the class they are an inner class of.

Let's get back to our Parrot shall we?

class Parrot(val name: String, var age: Int): Animal {

    //vals and vars useful for a parrot

    inner class HealthyConditions {
        val featherColour: String // Does not really apply to Dogs, Cats, Fish, etc.
        val cageSize: Size // Does not really apply to Dogs, Cats, Fish, etc.
        val weightInGrammesByAge: Map<Int, Int>
        val dailySleepInHoursByAge: Int<Int, Int>
        
        /** variable 'age' from the Parrot class can be accessed only because the HealthyConditions class is marked inner!  */
        fun isUnderweight(currentWeightInGrammes: Int): Boolean {
            return weightInGrammesByAge[age] > currentWeightInGrammes
        }
    }
}

How much a healthy Parrot should weigh changes over the course of the said parrot's life. A hatchling weighing a couple hundreds of grams is fine but a grown individual under 2 kilograms might need some attention. Therefore, the question of whether or not a Parrot is underweight at X grams cannot be answered easily, but should we ask if Bob the Parrot should gain some weight, we could use what we know about Bob to determine the answer. In order to do that, the HealthyConditions class will be accessed through Parrot("Bob", 5).HealthyConditions().


Now, to top it off, you might still wonder if accessing parent class properties is really useful for the inner class. After all, you could simply provide the age value wherever you call the isUnderweight() function, right? That is true, of course, but when discussing nested classes we decided each Animal deserves their own HealthyConditions implementation. For a Dog class, the breed of the dog is just as important in determining its correct weight as is its age. For yet other species, the gender will also be of importance. Meaning that, without direct access of the inner class to its parent's properties, Healthcheck interface's function checkIfUnderweight() would have to accept awfully lots of different, probably nullable, variables in order to be applicable to all different kinds of animals in the system.


I took a lot of time talking over the concept without many code samples but from what I understood you do not struggle with the implementation, but with the justification for even implementing the code in the first place. Also, forgive me if the "Animal classes" example I came up with may appear dumbed down - the truth is it was the first I came up with. ;)

Anyways, I hope this short read may help you understand the concept and its use cases better. Safe coding and may the bugs stay away from you!

ryfterek
  • 669
  • 6
  • 17
  • 2
    Very good write up. Loved the example of `Parrot` and living conditions, something different! – Yogesh Umesh Vaity Jan 30 '21 at 15:20
  • 4
    @YogeshUmeshVaity thank you for leaving a kind word behind! Glad my answer is still relevant and useful. Took some time now to re-read this 2yo entry and I've fixed a couple of grammar mistakes. – ryfterek Jan 30 '21 at 15:42
  • Hi, I'm new to OOP, after read your answer I got a new question. why can't we just declare a class ParrotHealthyConditions without nest the class inside Parrot?and it should be something like `class Parrot { val healthCondition:ParrotHealthyCondition? = null}` – Archsx May 19 '23 at 05:41
  • @Archsx hi! We can, of course, forego the capability of nesting classes in one another and define the class as you propose. However, as I noted in my original response, given the opportunity, we can nest classes in one another if we see that there is an obvious hierarchy between two classes that makes one a superior construct over the other. A very common practical example of class nesting is the Builder pattern, where we nest a class `Builder` in another class to logically denote that said Builder is nothing but a utility of the class it is nested in. – ryfterek Jun 02 '23 at 22:26
9

The official Kotlin documentation is quite self-explanatory:

A nested class marked as inner can access the members of its outer class. Inner classes carry a reference to an object of an outer class:

Perhaps you do not understand what this means.

Concretely, in your code sample, you can only access welcomeMessage which is defined in Outer from code in Nested when Nested is marked as inner. Try removing inner, and your code won't compile.

Steven Jeuris
  • 18,274
  • 9
  • 70
  • 161
  • 3
    I realize this does not explain use cases for this feature. But, given that the OP erroneously believed that "nested class can perform the same operation as that of inner class", I feel this should suffice as an answer. When to use this feature is a different question. I can list many more examples that list "Hello world" which use different language features. This should not constitute as a reason why any of these exist. – Steven Jeuris Dec 21 '19 at 22:12