Upcasting class
What is upcasting?
Taking an object reference and treating it as a reference to its base type is called upcasting.
The term upcast refers to the way inheritance hierarchies are traditionally represented with the base class at the top and derived classes fanning out below.
Inheritance and adding new member functions is the practice in Smalltalk, one of the first successful object-oriented languages in Smalltalk, everything is an object and the only way to create a class is to inherit from an existing class, often adding new member functions. Smalltalk heavily influenced Java, which also requires everything to be an object.
Kotlin’s way
Kotlin frees us from these constraints. We have stand-alone functions so everything doesn’t need to be contained within the classes. Extension functions allow us to add functionality without inheritance. Indeed, requiring the open
keyword for inheritance makes it a very conscious and intentional choice, not something to use all the time.
why upcast
Let’s imagine this. We have 3 objects (Circle
, Square
and Triangle
) that inherit the Shape
. And we have a function -
fun show(shape: Shape) = print(shape.toString)
graph BT
B(Circle) --> D(Shape)
C(Square) --> D
A(Triangle) --> D
When we pass a Circle
, Square
or Triangle
as an argument of type Shape
in the show()
, we cast up the inheritance hierarchy. In the process of upcasting, we lose the specific information about whether an object is of type Circle
, Square
or Triangle
. It becomes nothing more than a Shape
object.
Treating a specific type as a more generic type is the entire point of inheritance. The mechanics of inheritance exist solely to fulfil the goal of upcasting to the base type. Because of this abstraction (aka everything is a Shape
), we can write a single show
function of writing one for each of every type of elements. Upcasting is a way to reuse the code for objects.
Composition over inheritance
Indeed, in virtually every case where there’s inheritance without upcasting, inheritance is being misused – it’s unnecessary, and it makes the code needlessly complicated. The misuse is the reason for the maxim in Software Design:
Prefer composition over inheritance
If the point of inheritance is the ability to substitute a derived type for a base type, what happens to the extra member functions: color()
in Square
and rotate()
in Triangle
?
Liskov Substitution Principle says that after upcasting, the derived type can be treated exactly like the base type. This means that any member functions added to the derived class are, in effect, “trimmed”. They still exist, but because they are not part of base class interface, they are unavailable.
Summary
Upcasting is a way to reuse the code for objects. One important thing to take note is that after the upcast, you can only call members of the base type.