Methods
Methods, also known as member functions, are functions defined inside the body of a class. They have implicit access to the properties of a class, by virtue of being defined within the same scope as those properties.
The syntax for defining a method is basically the same as that for standalone functions. You can write a method with a block body or expression body, as you prefer. The method can accept input via its parameter list and return values to the caller in the same way as a standalone function.
Task 12.4.1
This task will give you more practice at implementing methods. The code
is provided as an Amper project, in the subdirectory tasks/task12_4_1 of
your repository.
-
Examine
Point.kt, in thesrcsubdirectory of the project. -
Edit
Circle.kt. In this file, implement a new class namedCircle. Give your class twovalproperties:centre, aPointobject representing coordinates of the circle’s centre; andradius, aDoublevalue representing the circle’s radius.Check that project code compiles with
./amper buildRepeat this after each of step of developing
Circle. -
Add an
initblock toCirclethat guaranteesradiuswill be greater than zero when aCircleobject is created. -
Add methods
area()andperimeter()toCircle. These methods should have no parameters. They should return the area and perimeter, respectively, of the circle.Area and perimeter can be computed using the formulae \( \pi r^2 \) and \( 2\pi r \), respectively. The constant \( \pi \) can be imported as the name
PI, withimport kotlin.math.PI -
Add a method
contains()toCircle. This should have a single parameter, of typePoint. It should returntrueif the given point is inside the circle,falseotherwise. The point should be considered inside if the distance from it to the centre of the circle is less than or equal to the radius.If you like, use
infixwhen defining the method, so that it can be invoked like this:if (circle contains point) { ... } -
Edit
Main.kt. In this file, write a program that creates aCircleobject and then demonstrates the use of its three methods.Run the program with
./amper run
Overriding
One feature of methods that they don’t share with standalone functions is their ability to override (i.e., substitute for) other methods, or be overridden themselves. We will consider a simple example of this here, deferring more detailed discussion to when we cover inheritance.
All classes in Kotlin inherit implicitly from a superclass named Any.
This provides all Kotlin classes with certain capabilities, one of them
being the ability to generate a string representation of an object, via
the toString() method. The default representation is generic and not
particularly useful, so it is common to override toString() with a
version that yields something better1.
Note: toString() is called automatically on any object you pass to
print() and println(), so overriding it can be very useful for tasks
like debugging.
Task 12.4.2
-
Edit
Point.kt, in thetasks/task12_4_2subdirectory of your repository. Add amain()function that creates aPointobject and then callsprintln()on that object.Compile and run the program. What do you see?
-
Add a new method to the
Pointclass:override fun toString() = "($x, $y)" -
Recompile the program and run it again. What has changed?
You must use the override keyword when overriding a method, and the
new version must have the same signature as the version being overridden.
Also, note that methods must grant explicit permission to be overridden
in subclasses. The example above works because the Any superclass grants
that permission for toString(). We will discuss this further when we
cover inheritance.
Method or Extension Function?
Almost all of the methods we’ve seen so far could have been written instead
as extension functions. For example, Point
could have been implemented like this:
class Point(var x: Double, var y: Double)
fun Point.distance() = hypot(x, y)
fun Point.distanceTo(p: Point) = hypot(x - p.x, y - p.y)
From the perspective of users of Point, there is no discernable difference
between this version of the class and the version with methods rather than
extension functions.
But methods have some unique advantages. As we’ve seen already, they support overrriding, whereas extension functions do not.
Another advantage is that methods have more privileged access to members of a class. A method can make use of anything defined in its class, including private members. By contrast, an extension function only has access to the public API of the class.
Extension functions have their uses, though. If you don’t have access to the source code of a class, and that class prevents inheritance, then extension is the only way in which you can add new functions to that class.
-
You may remember doing something similar for Python. Python classes have inherited ‘dunder’ methods, much like Kotlin classes. Overriding the
__str__method in a Python class is the equivalent of overridingtoString()in a Kotlin class. ↩