This is the part 7 of series Java to Kotlin - Learning Simplified. I hope you are learning something new from every part. This part shows utilities from Kotlin which might win your heart.
If you have not read part 6, please do here.
Index of this Part: Extension Function, Data Class, Constant, Singleton, Anonymous, Companion
Extension Function
How many times have you felt there should be abc function in xyz class? I felt the need of it many times. Mostly it is contextual and project dependent. For example, I might need a function in String class which camelcases every word OR I might need a function in Employee class of a library which logs the zipcode and city. There is a famous way in Java, Static Utilities. Here is a java snippet:
// Java, we use static utilities
class Util {
public static String toCamelCase(String input){
// do the magic
return camelCaseString;
}
public static printFormattedAddress(Employee emp) {
// fetch the city from zipcode
// print the formatted address
}
}
π‘ The above code is legit and works fine. Infact everything in life is fine unless we find a better alternative. There is no denying the above code becomes cluttered. The util class has many methods from various contexts and it is no OOPS.
Kotlin claims: I can make it more readable. How about I give you power to create a function in a class even though you donβt own it (could be platform class, could be a library class) π€―
In the example above, how will you feel if I say you can call input.toCamelCase()
? I bet you will find it more readable. Before we see an example, let me clarify something:
In Java OR Kotlin, if you do not have access to a class YOU simply can not add methods in it
The Kotlin extension function is an illusion
and a smart syntax.
π₯ Define an extension function
Anywhere in your project, you can add an extension function to any class by the syntax fun ClassName
dotfunctionName
. In our example above, we can say fun String.toCamelCase()
// Kotlin, Declare extension function
fun String.toCamelCase(){
// do your magic here, write code as you are in string class
}
Now you can call this function as a normal function:
// Kotlin, calling extension function
val name:String ="gaurav khanna"
val camelCased = name.toCamelCase() // π₯
π€― But how is it possible? JVM does not allow that and Kotlin is a JVM based language. The simple explanation is illusion
. Kotlin follows the Java route and creates a static funtion under the hood. It calls the static function and passes the instance you used to call the extension function. In the camelCase example:
// Kotlin under the hood
fun String.toCamelCase(){} // extension function
// is converted to static like Kotlin flavor
fun toCamelCase(String instance){}
π You can define an extension function with same signature as a member function but the member function always wins
π Overloading is a good use case for extension functions
π Extension function can be defined in top-level OR inside another class. Top level are accessible everywhere and the latter only inside the same class
Note: This section is a glimpse to extension function. Check official docs to see detailed documentaion. π₯ Extensions can be used for properties as well. Thatβs your home work.
Data Class
This is one more my favourite feature. In Java, we hava notion of POJO/model which we use to save data. Employee
can be a model class carrying only data and no logic. As we know Java is verbose, we need to define member variables
, βgetter-setterβ , equals, hashcode
etc. Here is a Java snippet:
// Java
public class Employee{
public final String name;
privaye int age;
public Employee(String name, int age){
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return age == employee.age &&
Objects.equals(name, employee.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Employee1{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Get ready to blow your mind π€―, Kotlin equivalent is:
// Kotlin
data class Employee(val name:String, var age:Int){}
π₯ π₯ π₯ Thatβs it! You got everything with 1 line which we just wrote in Java (~50 lines)
data class
offers member declarion, assignment, toString, equals, hashcode, getter, setter etc. It also offers Destructure Declaration which might confuse you at this stage. Read when you are more comfortable with Kotlin syntax. Dear java, π
Constant
In Java we define a constant with static fields, usually a Constant class and some static final fields inside. In Kotlin we have a keyword const
which is used to define a constant. Remember, Kotlin does not have static
keyword but the notion is same. Here is a snippet:
// Java
class Constants{
public static final int TIMEOUT = 200;
}
// Kotlin
const val TIMEOUT = 200
π‘ We do not need even a class in Kotlin, constant can be defined as top-level in a file
Singleton
Singleton as a design pattern and concept is very popular and useful. In Java if we need a class to be singleton, we need to write boilerplate code everytime. I never realised this before I encountered the Kotlin way. Kotlin has this concept a part of the language itself. Letβs see some code:
// Java, not threadsafe and verbose implementation on purpose
public class Factory{
private static Factory _instance;
private Factory(){}
public static Factory getInstance(){
if(_instance == null){
_instance = new Factory();
}
return _instance;
}
public void doSomething(){
}
}
and Kotlin is on fire π₯ again:
// Kotlin
object Factory{
fun doSomething(){}
}
π‘ yes, thatβs it! Now factory has only one instance and can be accessed with simple syntax Factory.doSomething()
It appears like static invocation in Java.
Anonymous class
In Java we use anonymous classes mostly when we are passing an object of an inteface at runtime. Kotlin has same notion with slightly different syntax. Java is more straightforward in this context and Kotlin is logical as well. Here is a snippet:
// Same Interface
interface Logger{
public void log(String text);
}
// Java
public void callLogger(Logger logger){}
public void doSomething(){
callLogger(new Logger(){
@Override
public void log(String text){
// code
}
});
}
In you notice, anonymous classes
are singleton by default. So Kotlin decided to use same same syntax object
// Kotlin
fun callLogger(logger:Logger){}
fun doSomething(){
callLogger(object: Logger{
@override fun log(text:String){
}
})
}
Anonymous classes are created with object: InterfaceName{}
Companion Object
In java, we have static variables and static methods. Kotlin does not have notion of static but there is a close syntax. As we saw above object
is used for singleton. There is another syntax companion object
which can be used inside a class and it serves the purpose of a class level singleton inside the class which indirectly can be accessed as static. Think them as class members
rather than instance members
. Here is a snippet:
// Kotlin
class Car (val name:String){
companion object Factory {
fun assembleCar(name:String){
// do some busines logic
return Car(name)
}
}
}
The above code can be accessed as Car.Factory.assembleCar("ABC")
, Smart kotlin allows to access without the Factory
word, so we can access it as: Car.assembleCar("ABC")
, truly class level members.
π‘ When defining the companion object, the name is optional (Factory
in the example above)
Singleton with Arguments
We saw in the above code how to define singleton in Kotlin, with object
syntax. If you notice, the singletons objects are without any members passed from outside. A few times, singleton can create the members itself but many a times, there are some dependencies we have to pass from outside. Solution is to make the class constructor private and use companion object to instantiate the instance. Similar to Jave singleton pattern. I will leave that for your exercise.
Summary
This part is full of Kotlin magic. We started with extension function
and realised it is more readable this way. Although it is an illusion and Kotlin uses static like function under the hood. The caller object is passed as receiver.
We saw the power of data class
in kotlin. It is truly the Kotlin concise and smart
flavor. It is used to define model/data classes. We get a lot of free offerings like toString, equals etc.
Then we encountered Constant
and Singleton
. Kotlin uses object
keyword to define singletons. Similarly object
keyword is used for anonymous
class. Finally we saw class level members with Companion objects
and how can we use it for singleton with arguments.