This is the part 4 of series Java to Kotlin - Learning Simplified. I hope you learn something new from every part. This part is dedicated to constructors and inheritance.
If you have not read part 3, please do here.
Index of this part: Constructors, Inheritance & constructor
Constructor
You must be wondering why did I introduce constructors so late. I wanted you to taste Kotlin and then gradually show you the power it offers. This is another area where your brain will go bizzare at first sight and then it will start making sense.
In Java, Constructor definition is very simple. Constructor is like a method without return type, If you think what can Kotlin offer here? After all this is a constructor. Letβs see a Java constructor:
// Java
public class Employee{
public final String name;
public final int age;
Employee(String name, int age){
this.name = name;
this.age = age;
}
}
Remember Concise, Smart and Powerful
principle? Kotlin applies all of the 3 in this context.
Kotlin Constructor - 1
: Letβs see similar constructor to Java.
class Employee constructor(name:String){
val name:String
init{
this.name = name
}
}
π€―π€―π€― <- Me When I saw it the first time, Letβs try to explain what is happening:
Kotlin says why to have a method like structure when we can combine the constructor with the class name. Be Concise
. Why do we need the keyword constructor? We do not need. So I am making it optional π
Without keyword constructor
class Employee(name:String){
val name:String
init{
this.name = name
}
}
What is init block?
π‘ This is a new concept in Kotlin. Everytime an instance of a class is created, init block will be executed. It is like the constructor body. 99% of the time all we do is initialise the member variables. Kotlin says, I am powerful
, If all you want is initialisation, why do you need init block! It is also optional.
Without constructor word and init block
class Employee(name:String){
val name:String = name
}
In fact as we know type inference, we do not need the type String
class Employee(name:String){
val name = name
}
Now check the difference between Java constructor and the one above this line. Kotlin has kept its promise of being concise and powerful. How about smart
?
Kotlin says if your member name is also same as the parameter name in the constructor, the assignment line is just overhead
class Employee(public val name:String){
}
By just typing val/var, Kotlin will understand you want to be concise and smart. It will create a same name member property and assign the value passed in the constructor
Remember, members are by default public
class Employee(val name:String){
}
This is a class Employee with a constructor accepts name String and there is a public final member name
which is assigned the value passed in the constructor All magic in one line.
π₯ And my friend, this class+constructor is equivalent to Java class+constructor we saw in the beginning. Concise, Smart and Powerful
If you think, the party is over. No, this is just the beginning. If your brain is going crazy, take a break. Drink green tea and watch some stand-up comedy.
Kotlin Constructor - 2
: Do you remember Java has a concept of polymorphism
. We can have many constructors and the caller is free to call whatever suits her. Here is an example with 3 constructors:
public class Employee{
public Employee(String name){}
public Employee(int age){}
public Employee(String name, int age){}
}
First thing first, Kotlin also allows to have multiple constructors in a class with a catch.
All the constructors have equal weight in Java
. None is superior to other. Kotlin has its own ideology here as well π€―. Kotlin divides the constructors in 2 categories. Primary and Secondary
.
The constructor we saw above is the Primary constuctor. Secondary constructor is defined in the same way as Java does but the catch here is YOU are required to call Primary constructor from the secondary. It is equivalent to call this()
constructor in Java world. In Java, it is optional to call another constructor as there is no weightage difference. None is superior. Letβs see a Java example:
// Java
public class Employee{
public final String name;
public final int age;
public Employee(String name){
this(name, -1)
}
public Employee(int age){
this("Not Defined", age)
}
public Employee(String name, int age){
this.name = name;
this.age = age;
}
}
In the above example, the 2 constructors are calling the 3rd constructor with some default value. Letβs see an equivalent code in Kotlin:
// Kotlin
class Employee(val name:String, var age:Int){ // <- primary constructor
// secondary constructors calling primary with `this`
constructor(age:Int):this("Not Defined", age){}
constructor(name:String):this(name, -1){}
}
Important Points to Note
π The secondary constructors are defined with the keyword
constructor
π It is mandatory to callPrimary constructor
π Primary constructor is called with the keywordthis
passing the parameters required (in our case, some sensible defaults)
π The syntax iscolon :
followed bythis(params)
Kotlin Constructor - 3
: As we saw before there is a notion of default value in Kotlin and constructors are no exception here. A constructor param can also have a default value
. This should be very easy to grasp. Here is a snippet:
// Kotlin
class Employee(val name:String, var age:Int = -1){
}
// Now the caller can skip passing age if she is happy with the default value
val emp1 = Employee("Gaurav") // valid
val emp2 = Employee("Gaurav", 28) // valid
val emp1 = Employee(age = 10) // invalid, name does not have default
π‘ If a class has neither primary nor secondary constructor, Kotlin adds a no-arg primary constructor like Java does
Inheritance & Constructor
As we saw in Part 2 of the series, we can extend a class in Kotlin. By default, all the classes are closed
and we need to mark it open
if we intend others to extend it.
The rule is If the super class has a constructor then the subclass is requried to call the constructor of the super class with either the super(params)
keyword OR after the primary constructor . Here is a snippet:
// Kotlin
open class Car(model:String){}
class RaceCar:Car(){
constructor(model:String):super(model){}
}
There are only two important rules when it comes to super constructor and inheritance. Super must be called and secondary constructor must call the primary constructor
Helper Rules:
π If superclass has a no-arg constructor, then super construcor is invoked from the class definition of the subclass OR the secondary constructor of the subclass
π If superclass does not have no-arg constructor then it is mandatory to call thesuper
with required params from the subclass
π Interesting part is if the subclass has a primary constructor then the secondary constructors of the subclass are requried to call the primary constructor, thus they can never call the super constructor
π In the case of subclass having primary constructor, only the primary constructor can call super constructor
π If subclass does not have any constructor then the super class constructor must be called from the class definition
Here are few examples to show the rules:
π If superclass has a no-arg constructor, then super construcor is invoked from the class definition of the subclass OR the secondary constructor of the subclass
// Kotlin
open class Car{}
class RaceCar:Car{ // invalid, super is not called
}
class RaceCar:Car(){ // valid, super is called
}
class RaceCar:Car{
constructor(){} // valid , Kotlin adds a super call
}
class RaceCar:Car{
constructor():super{} // valid , explicit super call
}
π If superclass does not have no-arg constructor then it is mandatory to call the super
with required params from the subclass
// Kotlin
open class Car(model:String){}
class RaceCar:Car{ // invalid, super is not called
}
class RaceCar:Car("Tesla"){ // valid, super called explicitly
}
class RaceCar:Car{
constructor():super("Tesla") // valid, super called explicitly
}
π Interesting part is if the subclass has a primary constructor then the secondary constructors of the subclass are requried to call the primary constructor, thus they can never call the super constructor
π In the case of subclass having primary constructor, only the primary constructor can call super constructor
// Kotlin
open class Car(model:String){}
class RaceCar(wings:Int):Car{ // invalid, super is not called
}
class RaceCar(wings:Int):Car{ // invalid, super is not called
constructor():super("Tesla") // invalid, primary constructor not called
}
class RaceCar(wings:Int):Car{ // invalid, super is not called
constructor():this(1) // primary called
}
class RaceCar(wings:Int):Car("Tesla"){ // valid, super is called
constructor():this(1) // primary called
}
π If subclass does not have any constructor then the super class constructor must be called from the class definition
// Kotlin
open class Car(model:String){}
class RaceCar:Car{ // invalid, super is not called
}
class RaceCar:Car("Tesla"){ // valid, super is called
}
Summary
As we saw Kotlin keeps its 3 promises, being Concise, Smart and powerful
. We can define the constructor with the class name and the constructor
keyword is optional. There is init
block which is executed as soon as the instance is created and primary constructor is invoked. We can define val/var
in the constructor and Kotlin will create same named members and assign the passed values.
There is a notion of primary and secondary
constructor in Kotlin. The secondary constructor is defined with the keyword constructor
and it is mandatory to call primary
constructor with colon : followed by this(params)
. We can not define val/var
in the secondary constructor.
Constructors can also have default values for the parameters.
There are only two important rules when it comes to super constructor and inheritance. Super must be called and secondary constructor must call the primary constructor. Kindly check the section.