Chapter 3: Defining and calling functions
Introduces the chapter’s focus on making Kotlin functions easier to declare and call, and on adapting Java libraries to Kotlin style.
After this chapter you will be able to
- Collection Creation Basics. Describe how the section introduces creating a set with setOf and says lists and maps are created similarly. Explain that a set’s order does not matter when comparing sets.
- Java Collection Compatibility. Explain that Kotlin uses standard Java collection classes rather than reimplementing them, so collections can pass between Kotlin and Java code without conversion. Distinguish those classes from Kotlin’s collection interfaces, which the section says are read-only by default.
- Custom collection formatting. Explain why a collection’s default string representation may be insufficient and describe how joinToString produces a chosen format.
- Named argument function calls. Explain why naming arguments improves the readability of a function call and describe how to use names when calling a function written in Kotlin.
- Defaults instead of overloads. Explain how default parameter values can replace multiple overloads, using joinToString’s comma separator and empty prefix and postfix as examples.
- Rules for omitting arguments. Distinguish which arguments may be omitted in a regular call versus a call using named arguments.
- Top-level functions and packages. Explain why Kotlin allows functions outside classes and how those functions are organized and called across packages.
- Java access to top-level functions. Describe how the JVM represents Kotlin top-level functions, how Java calls them, and how to change the generated class name.
- Extensions on existing classes. Explain that an extension function is defined outside a class but called like one of its members, letting Kotlin code add convenient operations to existing Java or Kotlin APIs without rewriting them.
- Receiver type and object. Describe how an extension declaration names its receiver type before the function name, and distinguish that type from the receiver object on which the function is called.
- Extension function import requirements. Explain why an extension function defined elsewhere is not automatically available throughout a project and how it can be imported.
- Conflicting extension import aliases. Explain how to use an import alias when same-named extension functions from different packages are needed in one file, and why a fully qualified call is not the alternative here.
- Java calls to extension functions. Explain that an extension function is compiled as a static method whose first argument is the receiver, and describe how Java calls or statically imports that method.
- Collection joinToString extension. Describe how joinToString is made available on collections and called like a member function, with default values for its arguments.
- Static extension receiver limits. Explain how the static nature of extensions permits a receiver restricted to collections of strings, and state the resulting limits on calls and overriding.
- Static resolution of extensions. Explain why calling an extension function on a View variable holding a Button calls the View extension, while calling an overridden member function such as click uses Button’s implementation.
- Extension property accessors. Explain how an extension property adds property-style access to a receiver type and why it must define a getter instead of storing a value.
- Collection API extensions. Explain why Kotlin can offer operations such as last and sum on collections that are instances of Java library classes.
- Vararg function parameters. Explain that a vararg parameter lets a function accept any number of values, which are packed into an array, and identify Kotlin’s vararg modifier as the syntax used instead of Java’s three dots.
- Spreading array arguments. Describe how to pass an existing array to a Kotlin vararg parameter by prefixing it with *, and explain that its elements can be combined with fixed values in the same call.
- Infix Call Syntax. Explain that `to` is a function call, not a built-in construct, and describe how an infix call places a function name between its target and argument. State which functions can use infix notation and what declaration modifier they need.
- Pairs in Map Creation. Describe how `to` creates a `Pair` and how `mapOf` takes a variable number of key-value pairs. Explain why map creation here is a regular function call despite its concise appearance.
- Destructuring Paired Values. Describe how a destructuring declaration initializes separate variables from a `Pair`, and identify the other examples given: a map entry and values used in a loop with `withIndex`.
- Kotlin and Java string interoperability. Explain why strings can pass between Kotlin and Java code without conversion.
- Regular expression splitting. Explain why splitting on a dot can give an unexpected result in Java, and how Kotlin makes regular-expression splitting explicit.
- Literal string delimiters. Describe how to split a Kotlin string on a dot or dash without a regular expression, including the available delimiter forms.
- Path parsing with substrings. Explain how Kotlin string functions can separate a full file path into its directory, filename, and extension without a regular expression.
- Triple-quoted regex path groups. Describe why the path regular expression uses a triple-quoted string and how its three groups correspond to path components.
- Multiline string contents. Explain what a triple-quoted string contains and how it differs from a regular string when writing line breaks and backslashes.
- Common indentation removal. Describe what trimIndent removes from a multiline string and why it is useful.
- Local functions for validation. Explain how extracting repeated validation code into a local function removes duplication without adding a separate method to the class.
- Save-specific extension validation. Justify moving the validation logic into a User extension function rather than adding a method to User, and state the section’s caution about nesting it locally.
What it builds on
- Using the when expression with arbitrary objects (chapter 2) · The earlier use of a set of colors.
- Declaring functions with parameters and return values (chapter 2) · Functions have named parameters that receive argument values.
- Kotlin source code layout: Directories and packages (chapter 2) · Assumes familiarity with Kotlin files and packages.
- Associating data with a class and making it accessible: Properties (chapter 2) · Assumes familiarity with properties, val, and var.
- Computing properties instead of storing their values: Custom accessors (chapter 2) · Custom property accessors
- Iterating over maps (chapter 2) · It recalls iterating over maps and their entries.
- Kotlin is interoperable (chapter 1) · Kotlin's interoperability with Java
- Easier string formatting: String templates (chapter 2) · String templates and embedded expressions
Section by section
Introduction
Introduces the chapter’s focus on making Kotlin functions easier to declare and call, and on adapting Java libraries to Kotlin style.
3.1 Creating collections in Kotlin
This section introduces creating Kotlin collections and explains how they use Java collection classes while offering Kotlin operations on them.
- Collection Creation Basics: Describe how the section introduces creating a set with setOf and says lists and maps are created similarly. Explain that a set’s order does not matter when comparing sets.
Why it matters: These creation functions give you collections to work with in the rest of the chapter. - Java Collection Compatibility: Explain that Kotlin uses standard Java collection classes rather than reimplementing them, so collections can pass between Kotlin and Java code without conversion. Distinguish those classes from Kotlin’s collection interfaces, which the section says are read-only by default.
Why it matters: It explains why Java collection experience transfers to Kotlin and why passing collections across the language boundary is straightforward. - Additional Collection Operations: Describe the section’s claim that Kotlin lets you do more with the standard Java collection classes, using its examples of getting a list’s last element, shuffling a list, or summing numbers. State that to, used in the map example, is an ordinary function rather than special syntax.
Why it matters: It shows that using Java collection classes does not limit the collection operations available in Kotlin.
Key terms: setOf, set, list, map, standard Java collection classes, collection interfaces, read only, last, sum, to
3.2 Making functions easier to call
The section motivates a customizable collection-formatting function and describes its initial implementation before later changes to make it easier to call.
- Custom collection formatting: Explain why a collection’s default string representation may be insufficient and describe how joinToString produces a chosen format.
Why it matters: This explains both the need for the function and what its formatting arguments control. - Generic formatting function: State what kinds of collections the described joinToString function can handle.
Why it matters: The same formatting function can be used for collections with different element types.
Key terms: toString, joinToString, StringBuilder, separator, prefix, postfix, generic, collections
3.2.1 Named arguments
This section shows how named arguments make Kotlin function calls easier to read and can allow arguments to be reordered.
- Named argument function calls: Explain why naming arguments improves the readability of a function call and describe how to use names when calling a function written in Kotlin.
Why it matters: Named arguments help a reader understand a call without having to look up the function’s parameter order.
Key terms: named arguments, parameter names
3.2.2 Default parameter values
This section shows how default parameter values reduce the need for Kotlin overloads, how callers omit arguments, and how to support Java callers.
- Defaults instead of overloads: Explain how default parameter values can replace multiple overloads, using joinToString’s comma separator and empty prefix and postfix as examples.
Why it matters: Defaults can make an API shorter to define and clearer to call. - Rules for omitting arguments: Distinguish which arguments may be omitted in a regular call versus a call using named arguments.
Why it matters: The call syntax determines which defaults you can select without supplying the other arguments. - Location of default values: Explain what happens to callers that omit a parameter when its default value changes and the function’s class is recompiled.
Why it matters: It explains why changing a default affects calls that leave that argument out. - Java callers and generated overloads: Describe why Java callers normally supply every parameter of a Kotlin function with defaults and what @JvmOverloads changes.
Why it matters: The annotation makes a Kotlin function with defaults more convenient to call from Java.
Key terms: default values, overloads, joinToString, regular call syntax, named arguments, trailing arguments, call site, @JvmOverloads, Java overloaded methods
3.2.3 Getting rid of static utility classes: Top-level functions and properties
This section shows how Kotlin replaces static utility classes with top-level functions and properties, and how those declarations appear to Java callers.
- Top-level functions and packages: Explain why Kotlin allows functions outside classes and how those functions are organized and called across packages.
Why it matters: You can write utility functions without inventing a class solely to hold static methods. - Java access to top-level functions: Describe how the JVM represents Kotlin top-level functions, how Java calls them, and how to change the generated class name.
Why it matters: The generated class name tells Java code how to call a Kotlin top-level function. - Top-level properties and constants: Distinguish how ordinary top-level properties and const top-level properties are exposed to Java.
Why it matters: The distinction helps you choose how a top-level value should appear to Java callers.
Key terms: top-level functions, package, static methods, Kt suffix, @file:JvmName, top-level properties, static field, accessor methods, const
3.3 Adding methods to other people’s classes: Extension functions and properties
Introduces extension functions as a way to call functions on existing classes with member-like syntax without changing those classes.
- Extensions on existing classes: Explain that an extension function is defined outside a class but called like one of its members, letting Kotlin code add convenient operations to existing Java or Kotlin APIs without rewriting them.
Why it matters: You can make existing APIs more convenient to use without changing their source. - Receiver type and object: Describe how an extension declaration names its receiver type before the function name, and distinguish that type from the receiver object on which the function is called.
Why it matters: The distinction helps you read both an extension’s declaration and a call to it. - Receiver access and encapsulation: Explain how an extension function uses its receiver within its body and state the access limit that distinguishes it from a class member.
Why it matters: Member-like call syntax does not give an extension the access privileges of a real class member.
Key terms: extension function, member, final, receiver type, receiver object, this, receiver, private, protected, encapsulation
3.3.1 Imports and extension functions
This section explains how extension functions are imported and how import aliases resolve naming conflicts between them.
- Extension function import requirements: Explain why an extension function defined elsewhere is not automatically available throughout a project and how it can be imported.
Why it matters: An extension function must be in scope before you can call it. - Conflicting extension import aliases: Explain how to use an import alias when same-named extension functions from different packages are needed in one file, and why a fully qualified call is not the alternative here.
Why it matters: Aliases let you use conflicting extensions in the same file without ambiguity.
Key terms: extension function, import, * imports, as keyword, short name, fully qualified name
3.3.2 Calling extension functions from Java
Explains how Kotlin extension functions appear as static methods to Java callers and how to invoke them.
- Java calls to extension functions: Explain that an extension function is compiled as a static method whose first argument is the receiver, and describe how Java calls or statically imports that method.
Why it matters: This lets you call Kotlin extensions from Java without treating them as instance methods or expecting an adapter.
Key terms: extension function, receiver object, static method, static import
3.3.3 Utility functions as extensions
This section turns joinToString into a collection extension with default arguments and explains two consequences of extensions’ static nature.
- Collection joinToString extension: Describe how joinToString is made available on collections and called like a member function, with default values for its arguments.
Why it matters: This shows how an extension can make a utility function convenient to call on collections. - Static extension receiver limits: Explain how the static nature of extensions permits a receiver restricted to collections of strings, and state the resulting limits on calls and overriding.
Why it matters: A member-like call does not make an extension an overridable member or make it applicable to every collection.
Key terms: joinToString, extension, collection, default values, extension functions, static method calls, receiver type, collections of strings, overridden
3.3.4 No overriding for extension functions
This section contrasts overriding of member functions with static resolution of extension functions and explains why a member function takes precedence over an extension with the same signature.
- Static resolution of extensions: Explain why calling an extension function on a View variable holding a Button calls the View extension, while calling an overridden member function such as click uses Button’s implementation.
Why it matters: It lets you predict which function a call will use when a variable holds an instance of a subclass. - Member function precedence: State what happens when a class has a member function with the same signature as an extension function, and describe the consequence of adding such a member to an existing API.
Why it matters: An API change can silently redirect a client’s calls away from its extension function.
Key terms: extension function, member function, declared static type, run-time type, receiver, same signature, shadowed
3.3.5 Extension properties
This section shows how extension properties provide property-style access to existing classes without adding stored state, including mutable properties and Java access.
- Extension property accessors: Explain how an extension property adds property-style access to a receiver type and why it must define a getter instead of storing a value.
Why it matters: It explains both the convenient property syntax and the limits of what an extension property can hold. - Mutable extension properties: Describe when an extension property can be declared as a var, using StringBuilder as the section’s example.
Why it matters: A mutable extension property can offer assignment syntax without adding a field to the receiver. - Java access to extension properties: Describe how Java code accesses an extension property’s value or assigns through a mutable one.
Why it matters: It prevents confusion when calling a Kotlin extension property from Java.
Key terms: extension property, receiver type, custom accessors, backing field, var, StringBuilder, getter, setter, getLastChar, setLastChar
3.4 Working with collections: varargs, infix calls, and library support
This introduction previews collection-related standard-library functions and language features covered in the following subsections.
3.4.1 Extending the Java collections API
This subsection explains how Kotlin adds collection operations to Java collection classes through standard-library extension functions and how to discover those operations.
- Collection API extensions: Explain why Kotlin can offer operations such as last and sum on collections that are instances of Java library classes.
Why it matters: It explains where Kotlin’s convenient collection operations come from without requiring different collection classes. - Discovering available functions: Describe how to find functions available for a collection or another object without memorizing the standard library.
Why it matters: You can find a useful operation when you need it instead of memorizing the library.
Key terms: extension functions, last, sum, Kotlin standard library, code completion, standard library reference, regular methods
3.4.2 Varargs: Functions that accept an arbitrary number of arguments
This section explains how Kotlin functions accept a variable number of arguments and how to pass array elements as those arguments.
- Vararg function parameters: Explain that a vararg parameter lets a function accept any number of values, which are packed into an array, and identify Kotlin’s vararg modifier as the syntax used instead of Java’s three dots.
Why it matters: It explains how calls such as listOf can take different numbers of arguments. - Spreading array arguments: Describe how to pass an existing array to a Kotlin vararg parameter by prefixing it with *, and explain that its elements can be combined with fixed values in the same call.
Why it matters: It lets you use values already held in an array alongside other arguments in one vararg call.
Key terms: varargs, vararg modifier, spread operator, *
3.4.3 Working with pairs: Infix calls and destructuring declarations
This section explains how infix calls create pairs for maps and how destructuring declarations unpack pairs and other values.
- Infix Call Syntax: Explain that `to` is a function call, not a built-in construct, and describe how an infix call places a function name between its target and argument. State which functions can use infix notation and what declaration modifier they need.
Why it matters: It lets you recognize concise Kotlin expressions as ordinary function calls. - Pairs in Map Creation: Describe how `to` creates a `Pair` and how `mapOf` takes a variable number of key-value pairs. Explain why map creation here is a regular function call despite its concise appearance.
Why it matters: It connects the compact syntax for map entries to ordinary functions and pair values. - Destructuring Paired Values: Describe how a destructuring declaration initializes separate variables from a `Pair`, and identify the other examples given: a map entry and values used in a loop with `withIndex`.
Why it matters: It helps you read declarations and loops that name the parts of a value directly.
Key terms: infix call, infix modifier, to, extension function, to function, Pair, mapOf, key, value, destructuring declaration
3.5 Working with strings and regular expressions
The section explains that Kotlin and Java use the same strings, while Kotlin offers a clearer API for working with them.
- Kotlin and Java string interoperability: Explain why strings can pass between Kotlin and Java code without conversion.
Why it matters: You can work with strings across Kotlin and Java code without adapting them first. - Clearer Kotlin string API: Describe how Kotlin makes working with Java strings more convenient.
Why it matters: Kotlin can offer a clearer way to work with strings without changing the strings themselves.
Key terms: Kotlin strings, Java strings, Kotlin standard library methods, extension functions, standard Java strings
3.5.1 Splitting strings
Explains why Java’s string splitting can be surprising and how Kotlin distinguishes regular-expression splitting from splitting on literal delimiters.
- Regular expression splitting: Explain why splitting on a dot can give an unexpected result in Java, and how Kotlin makes regular-expression splitting explicit.
Why it matters: It helps you tell when a delimiter will be treated as a pattern rather than a literal character. - Literal string delimiters: Describe how to split a Kotlin string on a dot or dash without a regular expression, including the available delimiter forms.
Why it matters: For simple separators, you can use literal delimiters without escaping a regular-expression pattern.
Key terms: split, regular expression, Regex, Pattern, split extension function, plain-text strings, character arguments, delimiters
3.5.2 Regular expressions and triple-quoted strings
Shows two ways to parse a file path into a directory, filename, and extension, including how triple-quoted strings and match results work in the regular-expression approach.
- Path parsing with substrings: Explain how Kotlin string functions can separate a full file path into its directory, filename, and extension without a regular expression.
Why it matters: Simple substring operations can make this path-parsing task easier to read than a regular expression. - Triple-quoted regex path groups: Describe why the path regular expression uses a triple-quoted string and how its three groups correspond to path components.
Why it matters: The string-literal choice makes the regex easier to write, while the groups determine which path component each match captures. - Successful match value extraction: Trace how the regex implementation obtains path-component variables from a successful match.
Why it matters: Capturing groups is useful only when the successful match's values are assigned to the path components.
Key terms: substring before, substring after, delimiter, String extensions, triple-quoted string, regular expression, groups, literal dot, match, destructured property
3.5.3 Multiline triple-quoted strings
Explains how multiline triple-quoted strings preserve text, how to format and interpolate them, and why they are useful for expected output in tests.
- Multiline string contents: Explain what a triple-quoted string contains and how it differs from a regular string when writing line breaks and backslashes.
Why it matters: This lets you predict exactly what text a multiline literal will contain. - Common indentation removal: Describe what trimIndent removes from a multiline string and why it is useful.
Why it matters: It makes a readable, indented literal produce the text you intended. - Templates without escape sequences: Explain how string templates can supply a literal dollar sign or an escaped Unicode symbol within a multiline string.
Why it matters: An embedded expression gives you characters that the raw text of the literal cannot express through escapes. - Expected multiline test output: Justify using multiline strings for expected structured output in tests.
Why it matters: Keeping expected output in the test can make multiline comparisons easier to write and read. - IDE highlighting inside literals: Describe the optional IDE support for structured text inside a triple-quoted string.
Why it matters: IDE language injection can make embedded structured text easier to read and check.
Key terms: triple-quoted strings, multiline string, escape sequences, trimIndent, common minimal indent, string templates, embedded expression, escaped Unicode symbol, expected output, multiline strings
3.6 Making your code tidy: Local functions and extensions
This section shows how local functions and extension functions can remove duplicated validation code while keeping its purpose and scope clear.
- Local functions for validation: Explain how extracting repeated validation code into a local function removes duplication without adding a separate method to the class.
Why it matters: Local functions help you avoid repetition without cluttering a class with narrowly useful methods. - Access to enclosing parameters: Describe why the local validation function does not need to receive the User object as an extra argument.
Why it matters: Direct access keeps a narrowly scoped helper from needing parameters that its enclosing function already has. - Save-specific extension validation: Justify moving the validation logic into a User extension function rather than adding a method to User, and state the section’s caution about nesting it locally.
Why it matters: An extension can keep a class’s API focused while making object-centered code convenient to write.
Key terms: local function, don’t repeat yourself (DRY), validation, local functions, enclosing function, parameters, variables, extension function, User.validateBeforeSave, private data
3.7 Summary
Where this is used later
- Nullable primitive types: Int?, Boolean?, and more (chapter 8) · Creating Kotlin collections.
- Read-only and mutable collections (chapter 8) · Examines read-only and mutable collection interfaces in more detail.
- Kotlin collections and Java collections are deeply related (chapter 8) · Develops the relationship between Kotlin and Java collections.
- Functions and properties that work with generic types (chapter 11) · The later discussion develops how generic functions work with generic types.
- Secondary constructors: Initializing the superclass in different ways (chapter 4) · Named argument syntax.
- Syntax for lambda expressions (chapter 5) · Named arguments provide an alternative that can clarify a lambda’s purpose.
- Creating your own annotation declarations (chapter 12) · Named arguments in Kotlin function calls.
- Initializing classes: Primary constructor and initializer blocks (chapter 4) · Default parameter values for functions.
Concepts explained in this chapter
- extensionAn extension function is a Kotlin function defined outside a class that can be called with member-like syntax on values of an existing receiver type.
- multiline stringA triple-quoted string is a Kotlin string literal delimited by three quotation marks that preserves line breaks and does not interpret ordinary escape sequences.
- receiver typeA receiver type is the class or interface type whose values can invoke an extension function or serve as the receiver for an extension function type.
- local functionA local function is a function declared within another function or local scope rather than at the top level or as a class member.
- regular expressionA regular expression is a pattern language for matching, searching, or transforming text according to character and repetition rules.
- default valueA default parameter is a function parameter with a value automatically supplied when the caller omits that argument.
- infix functionAn infix call is Kotlin call syntax that places a one-parameter function name between its receiver and argument without parentheses or other separators.
- data transformationmap is a Kotlin standard-library operation that transforms each element and collects the resulting values into a new collection.
- const qualifierThe const modifier declares a compile-time constant property of a permitted primitive type or String type.
- extension propertyAn extension property is a Kotlin property that provides property-like access to an existing type without storing state in that type.
- receiverA receiver is an entity that accepts a message or signal, or an object on which a method or command operates.
- interopJava interoperability is the ability of Kotlin code to interact directly with Java code and libraries, including exchanging compatible string values without conversion.
- named argumentNamed-argument syntax specifies function or constructor arguments by writing each argument's parameter name explicitly.
- collectionA collection is a container or aggregate that holds multiple elements and supports operations such as membership testing.
- destructuring declarationA destructuring declaration is Kotlin syntax that initializes multiple variables from the component values of an expression.
- overrideMethod overriding is the definition of a subclass member function that replaces an inherited implementation with the same signature.
- static typingStatic typing is a language discipline in which the type of every expression is determined at compile time.
- listMutableList is a Kotlin mutable collection type whose elements can be read and modified through its invariant type parameter.
- read-only collection interfaceA read-only collection interface exposes collection contents and traversal operations without exposing mutation operations through that interface.
- attributeAn annotation is a declaration-associated metadata construct whose availability and retention can be configured for source processing, compiled class files, or runtime access.
- encapsulationInformation hiding is the design principle of concealing implementation details behind a restricted public interface.
- open modifierThe open modifier is a Kotlin declaration modifier that permits a class or member to be inherited or overridden.
- genericsA generic function is a function declared with one or more type parameters so it can operate on values of different types.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
