Wednesday, October 4, 2017

Kotlin From Scratch: Classes and Objects

Kotlin From Scratch: Classes and Objects

Kotlin is a modern programming language that compiles to Java bytecode. It is free and open source, and promises to make coding for Android even more fun.  

In the previous article, you learned advanced uses of functions, such as extension functions, closures, higher-order functions, and inline functions in Kotlin. 

In this post you'll get an introduction to object-oriented programming in Kotlin by learning about classes: constructors and properties, casting, and more advanced class features that Kotlin makes easy. 

1. Classes

A class is a program unit that groups together functions and data to perform some related tasks. We declare a class in Kotlin using the class keyword—similar to Java. 

The preceding code is the simplest class declaration—we just created an empty class called Book.  We can still instantiate this class even if it doesn't contain a body using its default constructor.

As you can observe in the code above, we didn't use the new keyword to instantiate this class—as is usual in other programming languages. new is not a keyword in Kotlin. This makes our source code concise when creating a class instance. But be aware that instantiation of a Kotlin class in Java will require the new keyword. 

Class Constructors and Properties

Let's look into how to add a constructor and properties to our class. But first, let's see a typical class in Java:

Looking at our Book model class above, we have the following:

  • two fields: title and isbn
  • a single constructor
  • getters and setters for the two fields (fortunately, IntelliJ IDEA can help us generate these methods)

Now let's look at how we can write the preceding code in Kotlin instead:

A pretty tidy class! We've now reduced the number of lines of code from 20 to just 9. The constructor() function is called a secondary constructor in Kotlin. This constructor is equivalent to the Java constructor that we called when instantiating a class. 

In Kotlin, there is no concept of a field like you might be familiar with; instead, it uses the concept of "properties". For example, we have two mutable (read-write) properties declared with the var keyword: title and isbn in the Book class. (If you need a refresher on variables in Kotlin, kindly visit the first post in this series: Variables, Basic Types, and Arrays). 

  • Kotlin
    Kotlin From Scratch: Variables, Basic Types, and Arrays
    Chike Mgbemena

An amazing thing is that the getters and setters for these properties are auto-generated for us under the hood by the Kotlin compiler. Notice that we didn't specify any visibility modifiers to these properties—so by default, they're public. In other words, they can be accessed from anywhere.

Let's look at another version of the same class in Kotlin:

In this code, we've removed the secondary constructor. Instead, we declared a constructor in the class header called a primary constructor. A primary constructor doesn't have any place to put a block of code, so we utilize the init modifier to initialize incoming parameters from the primary constructor. Note that the init code block is executed immediately when the class instance is created.

As you can see, our code still has a lot of boilerplate. Let's reduce it further:

Our Book class is now just one line of code. That's really cool! Notice that in the primary constructor parameter list, we defined our mutable properties: title and isbn directly inside the primary constructor with the var keyword. 

We can also add default values to any of the class properties right inside the constructor.

In fact, we can also omit the constructor keyword, but only if it doesn't have any visibility modifier (public, private, or protected) or any annotations. 

A very neat class, I must say!

We can now create a class instance like this:

Accessing and Setting Properties 

In Kotlin, we can get a property by the class object book, followed by a dot separator ., then the property name title. This concise style of accessing properties is called property access syntax. In other words, we don't have to call the property getter method to access or call the setter to set a property in Kotlin—as we do in Java. 

Because the isbn property is declared with the var keyword (read-write), we can also change the property value using the assignment operator =.

Let's see another example:

Here, we updated the isbn parameter to be immutable (read-only) instead—by using the val keyword. We instantiated a class instance book and reassigned the title property the value "Things Fall Apart". Notice that when we tried to reassign the isbn property value to 1234, the compiler complained. This is because the property is immutable, having been defined with the val keyword. 

Java Interoperability

Be aware that by declaring a parameter with the var modifier inside the primary constructor, the Kotlin compiler (behind the scenes) has helped us to generate both the property accessors: getter and setter. If you use val, it will generate only the getter. 

This means that Java callers can simply get or set the property field by calling the setter or getter method of the property respectively. Remember, this depends on the modifier used to define the Kotlin property: var or val

Custom Getters and Setters

In this section, I'll show you how to create custom accessors (getters and setters) for a property in Kotlin if you want to. Creating a custom setter can be useful if you want to validate or verify a value before it's set to a class property. And a custom property getter can be useful when you want to change or modify the value that should be returned.  

Creating a Custom Setter

Because we want to create our own custom getter or setter for a property, we have to define that property in the class body instead of the constructor header. 

This is why we moved the mutable (read-write) title property into the class body and gave it a default value (or else it wouldn't compile).  

You can see we defined our own setter method set(value) for the title right below the property definition—note that you can't modify this set() method signature because this is what the compiler expects as a custom property setter function.

The parameter value passed to the set method represents the actual value that was assigned to the property by users—you can change the parameter name if you wish, but value is much preferred. We validated the value by checking if the value is empty. If empty, stop execution and throw an exception; otherwise, reassign the value to a special field variable.

This special field variable field inside the set method is an alias for the backing field of the property—a backing field is just a field that is used by properties when you want to modify or use that field data. Unlike value, you can't rename this special field variable.

Creating a Custom Getter

It's very easy to create a custom getter for a property in Kotlin. 

Inside the get method, we simply return a modified field—in our case, we returned the book title in uppercase. 

Note that each time we set a value to the title property, its set method block is executed—the same goes for the get method each time we retrieve it. 

If you want to learn about member functions for a Kotlin class (the kind of function that is defined inside a class, object, or interface), visit the More Fun With Functions post in this series. 

  • Kotlin
    Kotlin From Scratch: More Fun With Functions
    Chike Mgbemena

More on Constructors

As I discussed earlier, we have two types of constructors in Kotlin: primary and secondary. We have the freedom to combine both of them in a single class—as you can see in the example below:

Note that we can't declare properties inside a secondary constructor, as we did for the primary constructor. If we want to do this, we have to declare it inside the class body and then initialize it in the secondary constructor.  

In the code above, we set the default value of the new property for the class Car (remember, new is not a keyword in Kotlin)—we can then use the secondary constructor to change it if we want. In Kotlin, every secondary constructor must call the primary constructor, or call another secondary constructor that calls the primary constructor—we use the this keyword to achieve that. 

Note also that we can have multiple secondary constructors inside a class. 

If a class extends a superclass, then we can use the super keyword (similar to Java) to call the superclass constructor (we'll discuss inheritance in Kotlin in a future post). 

As I said earlier, for us to explicitly include a visibility modifier to a constructor in a class, we've got to include the constructor keyword—by default, constructors are public. 

Here, we made the constructor private—this means that users can't instantiate an object using its constructor directly. This can be useful if you want users to instead call another method (a factory method) to do the creation of objects indirectly. 

2. Any and Nothing Types

In Kotlin, the topmost type in the type hierarchy is called Any. This is equivalent to the Java Object type. This means that all classes in Kotlin explicitly inherit from the Any type, including StringInt, Double, and so on. The Any type contains three methods: equalstoString, and hashcode

We can also utilize the Nothing class in Kotlin in functions that always return an exception—in other words, for functions that don't terminate normally. When a function returns Nothing, then we know it's going to throw an exception. No equivalent type of this kind exists in Java. 

This can come in handy when testing error handling behaviour in your unit tests.   

3. Visibility Modifiers

Visibility modifiers help us to restrict accessibility of our API to the public. We can provide different visibility modifiers to our classes, interfaces, objects, methods, or properties. Kotlin provides us with four visibility modifiers:

Public

This is the default, and any class, function, property, interface, or object that has this modifier can be accessed from anywhere.

Private 

A top-level function, interface or class that is declared as private can be accessed only within the same file. 

Any function or property that is declared private inside a class, object, or interface can only be visible to other members of that same class, object, or interface. 

Protected

The protected modifier can only be applied to properties or functions inside a class, object, or interface—it can't be applied to top-level functions, classes, or interfaces. Properties or functions with this modifier are only accessible within the class defining it and any subclass. 

Internal 

In a project that has a module (Gradle or Maven module), a class, object, interface or function specified with the internal modifier declared inside that module is only accessible from within that module. 

4. Smart Casting

Casting means taking an object of another type and converting it into another object type. For example, in Java, we use the instanceof operator to determine whether a particular object type is of another type before we then cast it.

As you can see, we checked if shape instance is Circle, and then we have to explicitly cast the shape reference to a Circle type so that we can call methods of the circle type. 

Another awesome thing about Kotlin is the smartness of its compiler when it comes to casting. Let's now see a version in Kotlin.

Pretty neat! The compiler is smart to know that the if block will be executed only if the shape object is an instance of Circle—so the casting mechanism is done under the hood for us. We can now easily call properties or functions of the Circle type inside the if block. 

Here, the last condition after the && in the if header will be called only when the first condition is true. If the shape is not a Circle, then the last condition won't be evaluated. 

5. Explicit Casting

We can use the as operator (or unsafe cast operator) to explicitly cast a reference of a type to another type in Kotlin. 

If the explicit casting operation is illegal, note that a ClassCastException will be thrown. To prevent an exception from being thrown when casting, we can use the safe cast operator (or nullable cast operator) as?

The as? operator will try to cast to the intended type, and it returns null if the value can't be cast instead of throwing an exception. Remember that a similar mechanism was discussed in the Nullability section in Nullability, Loops, and Conditions post in this series. Read up there for a refresher.

  • Kotlin
    Kotlin From Scratch: Nullability, Loops, and Conditions
    Chike Mgbemena

6. Objects

Objects in Kotlin are more similar to JavaScript objects than Java objects. Note that an object in Kotlin is not an instance of a specific class!

Objects are very similar to classes. Here are some of the characteristics of objects in Kotlin:

  • They can have properties, methods, and an init block.
  • These properties or methods can have visibility modifiers.
  • They can't have constructors (primary or secondary).
  • They can extend other classes or implement an interface.

Let's now dig into how to create an object.  

We place the object keyword before the name of the object we want to create. In fact, we are creating singletons when we create objects in Kotlin using the object construct, because only one instance of an object exists. You'll learn more about this when we discuss object interoperability with Java. 

A singleton is a software design pattern that guarantees a class has one instance only and a global point of access to it is provided by that class. Any time multiple classes or clients request the class, they get the same instance of the class. You can check out my post about the singleton pattern in Java to learn more about it.

  • Android SDK
    Android Design Patterns: The Singleton Pattern
    Chike Mgbemena

You can access the object or singleton anywhere in your project—so long as you import its package. 

If you're a Java coder, this is how we typically create singletons:

As you can see, using the Kotlin object construct makes it concise and easier to create singletons. 

Objects in Kotlin can be utilized also to create constants. Typically in Java, we create constants in a class by making it a public static final field like this:

This code in Java can be converted to Kotlin more succinctly like this:

Here we declared the constant APIConstants with a property baseUrl inside a package com.chike.kotlin.constants. Under the hood, a Java private static final member baseUrl is created for us and initialized with the string URL. 

To use this constant in another package in Kotlin, simply import the package.

Java Interoperability

Kotlin converts an object to a final Java class under the hood. This class has a private static field INSTANCE which holds a single instance (a singleton) of the class. The following code shows how simply users can call a Kotlin object from Java. 

Here, a Java class called Singleton was generated with a public static final member INSTANCE, including a public final function myFunc().

To make the object function or property in Kotlin be a static member of the generated Java class, we use the @JvmStatic annotation. Here's how to use it:

By applying the @JvmStatic annotation to myFunc(), the compiler has turned it into a static function. 

Now Java callers can call it like a normal static member call. Note that using the INSTANCE static field to call members will still work.

7. Companion Objects

Now we've gotten to understand what objects are in Kotlin, let's dive into another kind of objects called companion objects. 

Because Kotlin doesn't support static classes, methods or properties like the ones we have in Java, the Kotlin team provided us with a more powerful alternative called companion objects. A companion object is basically an object that belongs to a class—this class is known as the companion class of the object. This also means that the characteristics I mentioned for objects also apply to companion objects. 

Creating a Companion Object

Similar to static methods in Java, a companion object is not associated with a class instance but rather with the class itself—for example, a factory static method, which has the job of creating a class instance. 

Here, we made the constructor private—this means that users outside the class can't create an instance directly. Inside our companion object block, we have a function create(), which creates a Person object and returns it. 

Invoking a Companion Object Function

companion object instantiation is lazy. In other words, it will be instantiated only when needed the first time. The instantiation of a companion object happens when an instance of the companion class is created or the companion object members are accessed. 

Let's see how to invoke a companion object function in Kotlin.

As you can see, this is just like invoking a static method in Java as normal. In other words, we just call the class and then call the member. Note that apart from functions, we can also have properties inside our companion object. 

Note also that the companion class has unrestricted access to all the properties and functions declared in its companion object, whereas a companion object can't access the class members. We can have an init code block inside a companion object—this is called immediately when the companion object is created. 

The result of executing the code above will be: 

Remember, only a single instance of a class companion object can ever exist. 

We're also free to provide our companion object with a name. 

Here, we gave it a name called Factory. We can then call it like this in Kotlin:

This style is verbose, so sticking with the previous way is much preferred. But this might come in handy when calling a companion object function or property from Java.

As I said earlier, like objects, companion objects can also include properties or functions, implement interfaces, and even extend a class. 

Here, we have an interface PersonFactory with just a single create() function. Looking at our new modified companion object, it now implements this interface (you'll learn about interfaces and inheritance in Kotlin in a later post). 

Java Interoperability

Under the hood, companion objects are compiled similarly to the way a Kotlin object is compiled. In our own case, two classes are generated for us: a final Person class and an inner static final class Person$Companion

The Person class contains a final static member called Companion—this static field is an object of the Person$Companion inner class. The Person$Companion inner class also has its own members, and one of them is a public final function called create()

Note that we did not give our companion object a name, so the generated static inner class was Companion. If we had given it a name, then the generated name would be the name we gave it in Kotlin. 

Here, the companion object in Kotlin has no name, so we use the name Companion provided by the compiler for Java callers to call it.

The @JvmStatic annotation applied on a companion object member works similarly to how it works for a regular object. 

Companion Object Extensions

Similarly to how extension functions can extend the functionality of a class, we can also extend the functionality of a companion object. (If you want a refresher on extension functions in Kotlin, visit the Advanced Functions tutorial in this series). 

  • Kotlin
    Kotlin From Scratch: Advanced Functions
    Chike Mgbemena

Here, we defined an extension function extFunc() on the companion object ClassA.Companion. In other words, extfunc() is an extension of the companion object. Then we can call the extension as if it's a member function (it's not!) of the companion object. 

Behind the scenes, the compiler will create a static utility function extFunc(). The receiver object as an argument to this utility function is ClassA$Companion

Conclusion

In this tutorial, you learned about basic classes and objects in Kotlin. We covered the following about classes:

  • class creation
  • constructors
  • properties
  • visibility modifiers
  • smart casting
  • explicit casting 

Also, you learned about how objects and companion objects in Kotlin can easily replace your static methods, constants, and singletons you code in Java. But that's not all! There is still more to learn about classes in Kotlin. In the next post, I'll show you even more cool features that Kotlin has for object-oriented programming. See you soon!

To learn more about the Kotlin language, I recommend visiting the Kotlin documentation. Or check out some of our other Android app development posts here on Envato Tuts+!

  • Android SDK
    Java vs. Kotlin: Should You Be Using Kotlin for Android Development?
    Jessica Thornsby
  • Android SDK
    Introduction to Android Architecture Components
    Tin Megali
  • Android SDK
    How to Use the Google Cloud Vision API in Android Apps
    Ashraff Hathibelagal
  • Android SDK
    What Are Android Instant Apps?
    Jessica Thornsby

No comments:

Post a Comment