The Programmers Guide To Kotlin - Inheritance |
Written by Mike James | ||||
Monday, 07 August 2017 | ||||
Page 2 of 3
InterfacesAlthough Kotlin is single inheritance it also supports Interfaces which, in most modern languages, provide a way to implement multiple inheritance in a supposedly safe way. Interfaces in Kotlin are similar to those found in Java 8 but if you know "classical" interfaces you might be surprised at how close to a class a Kotlin interface is. The original idea of an interface was that it would provide a specification of the methods that a class had to implement but it would provide no implementation. This provides some of the security of inheritance without the risk of inheriting methods that weren't appropriate for the derived class. The interfaced forced the programmer to implement all of the methods it declared from scratch thus ensuring that they were all perfectly suited to the derived class. A programmer using the derived class could be confident that it had use of all of the methods declared in the interface. For example, an interface called Printable could declare print and println methods for an object. Any object that implemented the Printable interface would be safe to call print or println on. An interface gives you a specification of what methods a class that implements it supports. However it is very tedious to have to reimplement most of the methods defined in the interface because they often don't vary much in what they have to do. In short interfaces often cause the programmer to go back to copy and paste inheritance. As a result interfaces have tended to evolve into something that looks increasingly like class based inheritance. That is they come with code that is inherited. This has resulted in the idea that there are two types of method in an interface a declaration without a code body, usually called abstract, and a fully implemented method. A Kotlin interface can have abstract and implemented methods and implemented properties but without backing fields. Of course interfaces aren't classes and can't have constructors and they can't have init blocks. This limits the complexity of the implementation of an interface to just a collection of basic methods that behave as if they had been copied and pasted into the class. To create and interface you simply proceed as for a class but use the keyword Interface. For example:
This defines an interface with two methods - myFunc1 which is virtual i.e. no implementation and myFunc2 which is fully implemented. Notice that you don't have to use the keyword open to allow inheritance. The only purpose an interface serves is to be inherited and so they are open by default as are all of the functions and properties they define. When a class inherits or implements an interface it gains all of the implemented methods and it has to implement any of the methods that aren't implemented. Notice that implementing the virtual methods is not optional and the program won't compile until all interface methods are fully implemented. As properties in Kotlin are implemented as getter and setter methods they are more or less the same as methods. You can declare a property in an interface and it can be abstract. In this case the class that implements the interface has to provide the property including any getter and setter. Alternatively you can implement its getter and setter method in the interface but these cannot reference a backing variable. What this means is that either the inheriting class has to implement the method using a backing variable or the property has implemented get and set that use values generated by expressions or other functions. For example:
Note: random is provided by a Java library function so you will need to add
ConflictsThe classical use of an interface is to provide a set of unimplemented function and property definitions that a class can inherit and implement. The sole use of classical interfaces is to govern what methods and properties you can rely on existing in a class. This more modern version of interface allows you to define some inheritable implementation. This is often more efficient but as a class can inherit multiply from different interfaces it can cause problems with multiple definitions of the same method or property. This is one of the reasons why multiple inheritance as supported in say C++ is generally thought not to be a good idea. If a class can inherit from multiple base classes the set of methods that are inherited might contain multiple definitions of a what is supposed to be a single method. In this case we need rules that resolve such conflicts. If a class implements two interfaces that define the same method then the class has to override the method and provide its own implementation. In other words clashes have to be resolved by redefining the method. Of course the new method can call one of the Interface methods. To do this use use the call
where interface is the name of the interface and method the name of the method you want to call. This mechanism is general and you can call any interface method in this way. OverridingOne of the basic things that you do with inherited methods and properties is to override them by providing the class's own implementation. In Kotlin you have to declare a method as open if it can be overridden because by default methods cannot be overridden. This again is an attempt to not accidentally fall into the complexities of inheritance without begin aware of them. By default inherited methods cannot be overridden i.e. redefined. You don't have to override a method marked as open, it is optional, but if you do you have to mark the overriding method as override. For example:
In this case an instance of MyClassB will have the version of the method that prints "My New function". If you miss out open or override the compiler will point this out. You can override properties in the same way, the overridable property is marked open and the overriding property as override. The types have to be compatible, but you can completely redefine the property otherwise with new getter and setter and initialization. You can override a val with a var property, but not the other way round. When overriding method implementation in an interface you have to use the override keyword just as for a class. By default an overridden method is open and can be overridden in a subsequent derived class. If you want to stop this all you have to do is use the final keyword:
Abstract ClassesClasses and interfaces should be enough to tackle any inheritance based design but for largely historical reasons we also have abstract classes and methods. An abstract class has abstract methods that are not implemented. Of course, you can't create an instance of an abstract class because it isn't complete. The only thing you can do with an abstract class is inherit it. That is, an abstract class only exists to act as a base class. If a derived class implements all of the abstract methods then it can be instantiated. Isn't an abstract class just another way of creating an interface? The answer is yes and no. A class that has nothing but abstract methods looks a lot like an interface but remember that you can only inherit from one class but from many interfaces. This means that interfaces can be set up to act like definitions of abilities that classes have to have. Abstract classes are used to much more define what a class is rather than just provide a set of abilities. A class is also more general than an interface and an abstract class can have all of the things that a non-abstract class can - constructors, methods and properties backed by variables. An abstract class is used within a standard class inheritance scheme where some of the methods or properties are so class specific that creating a derived class demands that they be reimplemented. You can think of the abstract methods defining the variation of the base class that the derived class implements. For example, the much over worked example of classes that represent animals might have an abstract makeSound method which has to be implemented by any derived class - a dog goes woof, a cat meows and a mouse squeaks. Here the sounds define the derived class and so makeSound is abstract so as to force inheriting classes to implement their own version.
<ASIN:1871962536> |
||||
Last Updated ( Sunday, 10 September 2017 ) |