14
Methods properties, which are constants and variables that are part of classes and objects. Methods, as you’ve already seen, are merely functions that reside inside a class or object. In this chapter, you’ll take a closer look at methods. As with properties, you’ll begin to design more complex classes and objects.
Method refresher
Consider
ArrayList.removeAt(). It pops the item at a given index off an instance of an array list:
val numbers = arrayListOf(1, 2, 3) numbers.removeAt(numbers.lastIndex) println(numbers) // > [1, 2]
Methods like
removeAt() help you control the data in the array list.
Comparing methods to getters and setters
With custom accessors, you saw in the last chapter that you could run code from inside a class within a property definition. That sounds a lot like a method. What’s the difference? It really comes down to a matter of style, but there are a few helpful thoughts to help you decide.
Turning a function into a method
To explore methods, you will create a simple model for dates called
SimpleDate. Be aware that the various Kotlin platforms, such as the JVM and JS, contain production-ready
Date classes that correctly handle many of the subtle intricacies of dealing with dates and times.
val months = arrayOf( "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ) class SimpleDate1(var month:String) fun monthsUntilWinterBreak(from: SimpleDate1): Int { return months.indexOf("December") - months.indexOf(from.month) }
class SimpleDate2(var month:String) { fun monthsUntilWinterBreak(from: SimpleDate2): Int { return months.indexOf("December") - months.indexOf(from.month) } }
val date2 = SimpleDate2("October") println(date2.monthsUntilWinterBreak(date2)) // > 2
date.monthsUntilWinterBreak() // Error!
Introducing this
A class definition is like a blueprint, whereas an instance is a real object. To access the value of an instance, you use the keyword this inside the class.
// 1 fun monthsUntilWinterBreak(): Int { // 2 return months.indexOf("December") - months.indexOf(this.month) }
val date3 = SimpleDate3("September") date3.monthsUntilWinterBreak() // 3
return months.indexOf("December") - months.indexOf(month)
Mini-exercise
Since
monthsUntilWinterBreak() returns a single value and there’s not much calculation involved, transform the method into a property with a customer getter.
Object methods
Like classes, Kotlin objects defined with the
object keyword can have member functions that refer to the object itself.
class MyMath { // 1 companion object { fun factorial(number: Int): Int { // 2 return (1..number).fold(1) { a, b -> a * b } } } } // 3 MyMath.factorial(6) // 720
Mini-exercise
Add a method to the
MyMath class that calculates the n-th triangle number. It will be very similar to the factorial formula, except instead of multiplying the numbers, you add them.
Extension methods
Sometimes you want to add functionality to a class but don’t want to muddy up the original definition. And sometimes you can’t add the functionality because you don’t have access to the source code. Just as for properties, it is possible to augment an existing class or object (even one you do not have the source code for) by adding methods to it.
fun SimpleDate.monthsUntilSummerBreak(): Int { val monthIndex = months.indexOf(month) return if (monthIndex in 0..months.indexOf("June")) { months.indexOf("June") - months.indexOf(month) } else if (monthIndex in months.indexOf("June")..months.indexOf("August")) { 0 } else { months.indexOf("June") + (12 - months.indexOf(month)) } }
val date = SimpleDate() date.month = "December" println(date.monthsUntilSummerBreak()) // > 6
fun Int.abs(): Int { return if (this < 0) -this else this } println(4.abs()) // > 4 println((-4).abs()) // > 4
Companion object extensions
If your class has a companion object, you can add extension methods to it by using the implicit companion object name
Companion, or by using the custom name if the companion object has one.
fun MyMath.Companion.primeFactors(value: Int): List<Int> { // 1 var remainingValue = value // 2 var testFactor = 2 val primes = mutableListOf<Int>() // 3 while (testFactor * testFactor <= remainingValue) { if (remainingValue % testFactor == 0) { primes.add(testFactor) remainingValue /= testFactor } else { testFactor += 1 } } if (remainingValue > 1) { primes.add(remainingValue) } return primes }
MyMath.primeFactors(81) // [3, 3, 3, 3]
Challenges
- Given the
Circleclass below:
import kotlin.math.PI class Circle(var radius: Double = 0.0) { val area: Double get() { return PI * radius * radius } }
val months = arrayOf( "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ) class SimpleDate(var month:String, var day: Int = 0) { fun advance() { day += 1 } } var date = SimpleDate(month = "December", day = 31) date.advance() date.month // December; should be January! date.day // 32; should be 1!
Key points
- Methods are behaviors that extend the functionality of a class.
- A typical method is a function defined inside of a class or object.
- A method can access the value of an instance by using the keyword
this.
- Companion object methods add behavior to a class instead of the instances of that class. To define a companion object method, you add a function in the class
companion objectblock.
- You can augment an existing class definition and add methods to it using extension methods.
Where to go from here?
Methods and properties are the things that make up your classes, instances, and objects. Learning about them as you have these last two chapters is important since you’ll use them all the time in Kotlin.