Chapter 4: Classes, objects, and interfaces
Introduces concrete differences in Kotlin class declarations and the language features that make classes more flexible and concise.
After this chapter you will be able to
- Class and interface defaults. Explain how Kotlin classes and interfaces differ from familiar Java defaults: interfaces can declare properties, declarations are final and public by default, and nested classes do not implicitly reference an outer instance.
- Concise class declaration features. State what data classes, native delegation, and the object keyword provide at the introductory level described here.
- Interface declarations and implementation. Describe how to declare an interface and implement its abstract methods in a class. Explain that Kotlin interfaces may contain method implementations but cannot contain state.
- Default methods and conflicts. Explain when an implementing class may keep an interface method’s default implementation and what it must do when two interfaces provide the same method. Describe how it can call a chosen inherited implementation.
- Final by default. Explain that Kotlin classes, methods, and properties are final by default, and why this protects against the fragile base class problem.
- Inheritance and override controls. Describe where to use open to permit subclassing and overriding, and how final can stop further overriding of an overridden member.
- Kotlin visibility boundaries. Describe what public, protected, and private declarations can be accessed from, including the visibility of top-level private declarations and declarations with no modifier.
- Module visibility versus packages. Explain what internal restricts access to and why Kotlin does not use packages as a visibility boundary.
- Accessible public signatures. Explain why a public declaration cannot expose a less-visible type in its signature, and describe the visibility limit on extension functions.
- Nested and inner class defaults. Distinguish the default behavior of a class declared inside another class in Java and Kotlin, and explain how Kotlin’s inner modifier and this@Outer syntax relate to the outer instance.
- Button state serialization failure. Explain why the Java ButtonState example fails to serialize and how declaring the helper class static fixes it.
- Sealed hierarchy restrictions. Describe how sealed classes and interfaces restrict direct subclasses or implementations, and distinguish this from an unrestricted interface hierarchy.
- Exhaustive when expressions. Explain why handling every sealed subtype removes the need for an else branch in a when expression, and what happens if a new subtype is added without a corresponding branch.
- Constructor and initializer roles. Distinguish Kotlin’s primary and secondary constructors by where they are declared, and describe what initializer blocks are for.
- Primary constructor initialization. Explain how a primary constructor supplies parameters and can initialize properties, and when an initializer block is used.
- Secondary constructors and superclass initialization. Describe how a class without a primary constructor declares secondary constructors and uses super() to call corresponding superclass constructors.
- Same-class constructor delegation. Trace how a secondary constructor can call another constructor of its own class with this(), and state where the delegation path must end when there is no primary constructor.
- Abstract interface property contract. Explain what an abstract property in a Kotlin interface requires of an implementing class and why the interface does not dictate how its value is obtained.
- Nickname implementation and evaluation. Distinguish the three implementations of nickname and explain when the computed values are obtained.
- Backing field in accessors. Explain how a custom setter can add behavior when a stored property changes, and how its accessors refer to the stored value.
- Backing field generation conditions. Distinguish a property that stores a value from one whose custom accessors calculate its value without storage.
- Property accessor visibility. Explain how an accessor’s visibility can differ from its property’s visibility, and why LengthCounter gives its public counter property a private setter.
- Compiler-generated method benefits. Explain how Kotlin compiler generation differs from IDE-generated code for routine class methods and why that difference matters.
- Value and reference equality. Distinguish Kotlin’s == from === and explain why Customer needs an equals override for instances containing the same data to compare equal.
- Equality and hash code consistency. Explain why hashCode must be overridden along with equals and trace why a HashSet can fail to find an equal Customer when their hash codes differ.
- Generated data class methods. Explain how the data modifier supplies standard methods and which properties determine generated equality and hash codes.
- Read-only data class properties. Justify the recommendation to use read-only properties in data classes, particularly when instances serve as hash-map keys.
- Copying instead of mutation. Describe the generated copy method and why copying can be preferable to changing an existing data-class instance.
- Generated forwarding and selective overrides. Describe how `by` delegates an interface implementation to another object, and how overriding selected methods lets a wrapper change behavior while leaving the rest delegated.
- Shared meaning of object. Explain that Kotlin’s object keyword defines a class and creates an instance of it at the same time.
- Object declaration and initialization. Explain how an object declaration defines a class and its single instance, and describe how that instance is initialized and accessed.
- Singleton suitability and dependency injection. Justify when an object declaration is suitable and when regular classes with dependency injection are preferable.
- Companion object access. Explain why Kotlin uses a companion object when a class-level function needs access to private members, and describe how its members are called.
- Factory method trade-offs. Describe why companion objects are suited to factory methods and how factory methods compare with multiple constructors in the examples discussed.
- Companion objects as interface instances. Explain how a companion object can implement an interface and be passed where an instance of that interface is required.
- Extensions on companion objects. Explain how an extension on a companion object provides class-name call syntax while keeping the function outside the class.
- Anonymous object creation. Explain how an object expression creates an anonymous object and how that differs from an object declaration. Describe how the resulting object can be used as an interface implementation.
- Access to enclosing variables. Explain how code inside an object expression can use variables from the function where it was created, including changing their values.
- Semantic safety for basic values. Explain why a plain Int is insufficient for an expense amount and how a UsdCent wrapper prevents callers from confusing USD cents with yen.
- Inline representation and object cost. Describe how to declare UsdCent as an inline class and why its run-time representation usually avoids the allocations of an ordinary wrapper.
What it builds on
- Encapsulating behavior and data: Classes and properties (chapter 2) · Basic class and property declarations.
- Smart casts: Combining type checks and casts (chapter 2) · Smart casts after type checks.
- Computing properties instead of storing their values: Custom accessors (chapter 2) · Properties with custom accessors.
- Adding methods to other people’s classes: Extension functions and properties (chapter 3) · Extension functions are functions added to the callable interface of another class.
- Using the when expression to deal with enum classes (chapter 2) · Using when expressions to handle different cases.
- Associating data with a class and making it accessible: Properties (chapter 2) · Properties declared in classes.
- Default parameter values (chapter 3) · Default parameter values for functions.
- Getting rid of static utility classes: Top-level functions and properties (chapter 3) · Top-level functions as an alternative to static utility methods.
Section by section
Introduction
Introduces concrete differences in Kotlin class declarations and the language features that make classes more flexible and concise.
- Class and interface defaults: Explain how Kotlin classes and interfaces differ from familiar Java defaults: interfaces can declare properties, declarations are final and public by default, and nested classes do not implicitly reference an outer instance.
Why it matters: These defaults affect how you read and design Kotlin class hierarchies. - Constructor and property flexibility: Describe the alternatives to Kotlin’s concise primary constructor and property syntax when initialization or accessors need more than the simple form.
Why it matters: The concise forms do not prevent you from expressing more involved initialization or property behavior. - Concise class declaration features: State what data classes, native delegation, and the object keyword provide at the introductory level described here.
Why it matters: These features explain how Kotlin avoids some repetitive class code and expresses object-based declarations.
Key terms: interfaces, final, public, nested classes, inner, primary constructor, initialization logic, accessors, data class, delegation pattern
4.1 Defining class hierarchies
This introduction previews Kotlin class hierarchies, modifier defaults, and sealed hierarchies without explaining them.
4.1.1 Interfaces in Kotlin
This section explains how to declare and implement Kotlin interfaces, handle default-method conflicts, and use those interfaces from Java.
- Interface declarations and implementation: Describe how to declare an interface and implement its abstract methods in a class. Explain that Kotlin interfaces may contain method implementations but cannot contain state.
Why it matters: These rules let you recognize what an interface requires from an implementing class. - Mandatory override modifier: Explain when Kotlin requires the `override` modifier and why that requirement helps prevent accidental overrides.
Why it matters: An explicit override makes an inherited-member implementation intentional and compiler-checked. - Default methods and conflicts: Explain when an implementing class may keep an interface method’s default implementation and what it must do when two interfaces provide the same method. Describe how it can call a chosen inherited implementation.
Why it matters: You can retain a useful default while resolving ambiguous inherited behavior explicitly. - Java implementation of Kotlin interfaces: Describe what a Java class must implement when it implements a Kotlin interface whose methods have bodies.
Why it matters: The Java implementation cannot assume it inherits the default behavior available to a Kotlin implementation.
Key terms: interface, abstract method, state, colon, override modifier, superclass, default implementation, showOff, super<Clickable>.showOff(), Java class
4.1.2 Open, final, and abstract modifiers: Final by default
This section explains Kotlin’s final-by-default inheritance rules, how to allow or forbid overrides, and how abstract classes and final properties fit those rules.
- Final by default: Explain that Kotlin classes, methods, and properties are final by default, and why this protects against the fragile base class problem.
Why it matters: It explains why Kotlin makes authors choose explicitly what subclasses may change. - Inheritance and override controls: Describe where to use open to permit subclassing and overriding, and how final can stop further overriding of an overridden member.
Why it matters: These modifiers let a class permit intended customization without exposing every behavior to overrides. - Final properties and smart casts: Explain why final-by-default properties make smart casts available in more situations, and state the property conditions given here.
Why it matters: The inheritance default also helps the compiler safely avoid manual casts. - Abstract class rules: Distinguish an abstract class and its abstract members from an instantiable class, including whether abstract members need open.
Why it matters: Abstract declarations express behavior that a subclass must supply rather than behavior it may optionally replace.
Key terms: final, fragile base class problem, open, override, smart casts, val, custom accessor, abstract class, abstract members
4.1.3 Visibility modifiers: Public by default
This section explains Kotlin’s visibility modifiers, how they protect APIs and module boundaries, and where their behavior differs from Java.
- Kotlin visibility boundaries: Describe what public, protected, and private declarations can be accessed from, including the visibility of top-level private declarations and declarations with no modifier.
Why it matters: These boundaries determine which parts of a class or file other code can depend on. - Module visibility versus packages: Explain what internal restricts access to and why Kotlin does not use packages as a visibility boundary.
Why it matters: Module boundaries let you hide implementation details without relying on package names. - Public API exposure: Explain why library authors may use explicit API mode despite Kotlin’s public-by-default convention, and state what the mode requires.
Why it matters: Explicit declarations help library authors avoid committing to an API by accident. - Accessible public signatures: Explain why a public declaration cannot expose a less-visible type in its signature, and describe the visibility limit on extension functions.
Why it matters: A callable API must not require types its callers cannot access, and extensions do not bypass encapsulation. - Java bytecode visibility differences: Distinguish Kotlin source-level visibility from what Java code can access after Kotlin is compiled, particularly for internal declarations.
Why it matters: Kotlin visibility rules do not always prevent Java callers from reaching the compiled declaration.
Key terms: public, protected, private, top-level declarations, internal, module, package-private, encapsulation, explicit API mode, public API
4.1.4 Inner and nested classes: Nested by default
This section contrasts Java and Kotlin nested classes, using button-state serialization to show why an implicit reference to the outer instance matters.
- Nested and inner class defaults: Distinguish the default behavior of a class declared inside another class in Java and Kotlin, and explain how Kotlin’s inner modifier and this@Outer syntax relate to the outer instance.
Why it matters: The distinction tells you whether a nested helper class retains access to an enclosing object. - Button state serialization failure: Explain why the Java ButtonState example fails to serialize and how declaring the helper class static fixes it.
Why it matters: The example shows a practical consequence of retaining an outer-instance reference even when serializing only the helper object.
Key terms: nested class, inner modifier, static nested class, this@Outer, ButtonState, Button, NotSerializableException, static
4.1.5 Sealed classes: Defining restricted class hierarchies
Explains how sealed classes and interfaces restrict a hierarchy so the compiler can check that a when expression handles every case.
- Sealed hierarchy restrictions: Describe how sealed classes and interfaces restrict direct subclasses or implementations, and distinguish this from an unrestricted interface hierarchy.
Why it matters: The restriction is what lets Kotlin know which cases a hierarchy can contain. - Exhaustive when expressions: Explain why handling every sealed subtype removes the need for an else branch in a when expression, and what happens if a new subtype is added without a corresponding branch.
Why it matters: An omitted case becomes a compiler error rather than a subtle bug hidden by a default branch.
Key terms: sealed modifier, sealed class, sealed interface, direct subclasses, module, when expression, exhaustive, else branch
4.2 Declaring a class with nontrivial constructors or properties
Introduces Kotlin’s distinction between primary and secondary constructors and the role of initializer blocks.
- Constructor and initializer roles: Distinguish Kotlin’s primary and secondary constructors by where they are declared, and describe what initializer blocks are for.
Why it matters: This distinction helps you recognize the different places Kotlin puts class initialization code.
Key terms: primary constructor, secondary constructor, initializer blocks
4.2.1 Initializing classes: Primary constructor and initializer blocks
This section explains how Kotlin primary constructors initialize classes, how defaults and inheritance affect construction, and when to restrict constructor access.
- Primary constructor initialization: Explain how a primary constructor supplies parameters and can initialize properties, and when an initializer block is used.
Why it matters: It lets you read both Kotlin’s concise class declarations and their more explicit initialization forms. - Constructor defaults and calls: Describe how to call a Kotlin constructor and what default parameter values change, including the parameterless-constructor case.
Why it matters: Defaults make Kotlin construction concise while affecting how libraries and Java code can instantiate a class. - Superclass constructor invocation: Distinguish constructor calls for superclasses from interface entries in a class’s supertype list.
Why it matters: The parentheses in a supertype list signal construction of a superclass, not implementation of an interface. - Private constructor access: Explain what making a primary constructor private prevents and identify the Kotlin alternatives mentioned for Java-style utility or singleton classes.
Why it matters: It clarifies when constructor visibility restricts instantiation and when Kotlin offers a more direct construct.
Key terms: primary constructor, constructor, init, initializer block, val, default values, parameterless constructors, @JvmOverloads, superclass constructor, base class list
4.2.2 Secondary constructors: Initializing the superclass in different ways
Explains when Kotlin needs secondary constructors and how they delegate to superclass or same-class constructors.
- Secondary constructors and superclass initialization: Describe how a class without a primary constructor declares secondary constructors and uses super() to call corresponding superclass constructors.
Why it matters: This lets a Kotlin subclass support the different initialization routes offered by a superclass. - Same-class constructor delegation: Trace how a secondary constructor can call another constructor of its own class with this(), and state where the delegation path must end when there is no primary constructor.
Why it matters: Delegation lets constructors share an initialization route without skipping superclass initialization. - Reasons for multiple constructors: Explain why default parameters and named arguments usually replace overloaded constructors in Kotlin, and identify the superclass-interoperability case where multiple constructors remain useful.
Why it matters: It helps you choose secondary constructors only when they serve a need that simpler parameter defaults do not.
Key terms: secondary constructor, constructor keyword, super(), primary constructor, this(), base class, default parameter values, named argument syntax, secondary constructors, Java interoperability
4.2.3 Implementing properties declared in interfaces
This section explains how classes implement interface properties, why interface properties cannot store state, and when a read-only property is preferable to a function.
- Abstract interface property contract: Explain what an abstract property in a Kotlin interface requires of an implementing class and why the interface does not dictate how its value is obtained.
Why it matters: The property is a contract, not a requirement to store data in a particular way. - Nickname implementation and evaluation: Distinguish the three implementations of nickname and explain when the computed values are obtained.
Why it matters: The choice between a getter and a stored value determines whether computation happens on every access or just once. - Interface properties with accessors: Explain how an interface can supply a property accessor without storing state, and distinguish that property from an abstract one.
Why it matters: An interface can provide property behavior without taking responsibility for storing its value. - Property versus function choice: State the section’s conventions for choosing a read-only property rather than a parameterless function.
Why it matters: These conventions help a property access behave as callers expect rather than hide costly or surprising work.
Key terms: abstract property, backing field, getter, primary constructor, override, custom getter, initializer, read-only property, function
4.2.4 Accessing a backing field from a getter or setter
This section explains how custom property accessors use a backing field and when the compiler generates one.
- Backing field in accessors: Explain how a custom setter can add behavior when a stored property changes, and how its accessors refer to the stored value.
Why it matters: You can add behavior such as logging a change without giving up a property’s stored value. - Backing field generation conditions: Distinguish a property that stores a value from one whose custom accessors calculate its value without storage.
Why it matters: It helps you tell whether a property stores its own data or computes its value from something else.
Key terms: backing field, field, getter, setter, default accessor implementation, val, mutable property
4.2.5 Changing accessor visibility
This section shows how to keep a property publicly readable while restricting writes to its owning class.
- Property accessor visibility: Explain how an accessor’s visibility can differ from its property’s visibility, and why LengthCounter gives its public counter property a private setter.
Why it matters: Clients can see the total without being able to make it incorrect.
Key terms: accessor, visibility modifier, getter, setter, private
4.3 Compiler-generated methods: Data classes and class delegation
The section explains why compiler-generated methods reduce boilerplate in Kotlin classes and identifies common methods that otherwise need mechanical implementations.
- Compiler-generated method benefits: Explain how Kotlin compiler generation differs from IDE-generated code for routine class methods and why that difference matters.
Why it matters: You can distinguish saving the effort of writing boilerplate from keeping boilerplate out of the codebase altogether. - Common object method purposes: Describe what equals, hashCode, and toString provide for an object.
Why it matters: These examples make clear which routine methods compiler generation can spare you from writing.
Key terms: Kotlin compiler, mechanical code generation, boilerplate code, equals, hashCode, toString, hash maps
4.3.1 Universal object methods
This section explains why a data-storing Kotlin class may need custom toString, equals, and hashCode methods, and how those methods affect string output, equality, and hash-based collections.
- Useful object string representations: Explain why you might override toString in a class such as Customer and what the override changes.
Why it matters: A useful string representation makes objects easier to recognize in logs and debugging output. - Value and reference equality: Distinguish Kotlin’s == from === and explain why Customer needs an equals override for instances containing the same data to compare equal.
Why it matters: The distinction determines whether your comparison tests matching data or the identity of an instance. - Equality and hash code consistency: Explain why hashCode must be overridden along with equals and trace why a HashSet can fail to find an equal Customer when their hash codes differ.
Why it matters: Without matching hash codes, a hash-based collection can fail to recognize an object that equals considers equal.
Key terms: toString, string representation, equals, ==, ===, reference comparison, hashCode, HashSet, hash code contract
4.3.2 Data classes: Autogenerated implementations of universal methods
Explains how Kotlin data classes generate standard methods and support copying, why read-only properties are recommended, and how they compare with Java records.
- Generated data class methods: Explain how the data modifier supplies standard methods and which properties determine generated equality and hash codes.
Why it matters: The distinction determines which values count when data class instances are compared or used as hash keys. - Read-only data class properties: Justify the recommendation to use read-only properties in data classes, particularly when instances serve as hash-map keys.
Why it matters: Stable data-class values make hash-based containers safer to use and shared code easier to reason about. - Copying instead of mutation: Describe the generated copy method and why copying can be preferable to changing an existing data-class instance.
Why it matters: Copying lets code work with changed values without modifying the instance other code already references. - Java record comparison: Distinguish Kotlin data classes from Java records in generated methods and structural restrictions, and state how Kotlin can declare a record for interoperability.
Why it matters: The comparison clarifies what data classes offer beyond Java records and what Kotlin-to-Java record interoperability requires.
Key terms: data modifier, data class, equals, hashCode, toString, primary constructor, val, var, immutable, HashMap
4.3.3 Class delegation: Using the by keyword
The section explains how Kotlin’s class delegation replaces repetitive decorator forwarding code while allowing selected methods to be overridden.
- Inheritance fragility and decoration: Explain why overriding an inherited class can be fragile and how a decorator adds behavior without relying on the wrapped class’s implementation details.
Why it matters: It explains why forwarding to another object can be safer than extending its implementation. - Generated forwarding and selective overrides: Describe how `by` delegates an interface implementation to another object, and how overriding selected methods lets a wrapper change behavior while leaving the rest delegated.
Why it matters: You can customize a wrapper without writing every forwarding method or depending on how the wrapped collection implements them.
Key terms: implementation inheritance, decorator design pattern, interface, class delegation, by keyword, override, MutableCollection
4.4 The object keyword: Declaring a class and creating an instance, combined
Introduces the object keyword’s shared meaning and briefly distinguishes its three uses.
- Shared meaning of object: Explain that Kotlin’s object keyword defines a class and creates an instance of it at the same time.
Why it matters: This shared idea connects the different uses of object. - Three uses of object: Distinguish object declarations, companion objects, and object expressions by the purposes briefly stated here.
Why it matters: These distinctions help you recognize which use of object the section is discussing.
Key terms: object keyword, class, instance, object declaration, singleton, companion object, factory methods, object expression, anonymous inner class
4.4.1 Object declarations: Singletons made easy
This section explains how Kotlin object declarations create singletons, where they are useful, and when a regular class is a better choice.
- Object declaration and initialization: Explain how an object declaration defines a class and its single instance, and describe how that instance is initialized and accessed.
Why it matters: This is the defining difference between an object declaration and a regular class whose instances you construct yourself. - Singleton interface implementations: Describe how an object declaration can serve as a single instance of a class or interface implementation, including when the object is declared inside a class.
Why it matters: A stateless comparator illustrates how a singleton can satisfy an interface without creating a new instance for each use. - Singleton suitability and dependency injection: Justify when an object declaration is suitable and when regular classes with dependency injection are preferable.
Why it matters: Choosing a singleton for a heavily connected component can make testing and configuration harder. - Java access through INSTANCE: Describe how Java code accesses a Kotlin object declaration.
Why it matters: The INSTANCE field explains how Kotlin singletons appear to Java callers.
Key terms: object declaration, object keyword, initializer blocks, Comparator, nested object, sortedWith, singletons, dependency injection, unit tests, INSTANCE
4.4.2 Companion objects: A place for factory methods and static members
The section explains how companion objects provide class-level access to private members and why they are useful for factory methods.
- Companion object access: Explain why Kotlin uses a companion object when a class-level function needs access to private members, and describe how its members are called.
Why it matters: This explains when a top-level function is insufficient and how to call the class-level alternative. - Factory method trade-offs: Describe why companion objects are suited to factory methods and how factory methods compare with multiple constructors in the examples discussed.
Why it matters: It helps you choose between named creation methods and constructors without assuming factories always create a new object.
Key terms: static, top-level functions, object declarations, companion object, private members, factory methods, private constructor, secondary constructors
4.4.3 Companion objects as regular objects
This section shows that companion objects are regular objects: they can be named, implement interfaces, support extensions, and interact with Java static members.
- Companion object names and access: Explain how a companion object is named and how its members are accessed through the containing class.
Why it matters: The class-name syntax works whether or not you name the companion object. - Companion objects as interface instances: Explain how a companion object can implement an interface and be passed where an instance of that interface is required.
Why it matters: A class can provide an interface-based factory through its companion object. - Java static interoperability: Distinguish ordinary companion-object access from Kotlin annotations used when Java requires a static member or field.
Why it matters: Companion-object syntax in Kotlin does not by itself guarantee the Java static API you need. - Extensions on companion objects: Explain how an extension on a companion object provides class-name call syntax while keeping the function outside the class.
Why it matters: You can keep format-specific code in another module without losing convenient class-name call syntax.
Key terms: companion object, Companion, JSONFactory, @JvmStatic, @JvmField, companion-object extensions, fromJSON
4.4.4 Object expressions: Anonymous inner classes rephrased
This section explains how object expressions create anonymous objects, how they differ from object declarations, and when they are useful for implementing listeners.
- Anonymous object creation: Explain how an object expression creates an anonymous object and how that differs from an object declaration. Describe how the resulting object can be used as an interface implementation.
Why it matters: This lets you supply a one-off implementation without naming a class. - Access to enclosing variables: Explain how code inside an object expression can use variables from the function where it was created, including changing their values.
Why it matters: A listener can update surrounding state, such as a click count. - Object expressions versus lambdas: Distinguish the situation where an object expression is most useful from the single-method-interface case where a lambda can be used.
Why it matters: It helps you choose a concise form for a one-off implementation.
Key terms: object expression, anonymous object, object declaration, MouseListener, variables, final variables, single-method interface, SAM conversion, function literal, lambda
4.5 Extra type safety without overhead: Inline classes
This section shows how Kotlin inline classes give basic values distinct meanings while usually avoiding the cost of wrapper objects.
- Semantic safety for basic values: Explain why a plain Int is insufficient for an expense amount and how a UsdCent wrapper prevents callers from confusing USD cents with yen.
Why it matters: A distinct type can catch a units mistake that an Int parameter cannot. - Inline representation and object cost: Describe how to declare UsdCent as an inline class and why its run-time representation usually avoids the allocations of an ordinary wrapper.
Why it matters: This explains how a meaningful type can avoid the usual cost of creating short-lived wrapper objects. - Inline class shape and capabilities: State the structural requirements for an inline class and distinguish its restrictions on inheritance from the members it can still provide.
Why it matters: These boundaries help you recognize when a wrapper can be an inline class and what behavior it may still expose.
Key terms: inline classes, UsdCent, type safety, value, @JvmInline, underlying type, wrapped property, primary constructor, interfaces, computed properties
4.6 Summary
Where this is used later
- Creating your own annotation declarations (chapter 12) · Basic Kotlin class declarations.
- Non-null types without immediate initialization: Late-initialized properties (chapter 7) · The distinction between a Kotlin property and its backing field
- Implementing your own delegated properties (chapter 9) · Accessing a property’s backing field from its setter
- Any and Any?: The root of the Kotlin type hierarchy (chapter 8) · The common methods available on Kotlin objects.
- Equality operators: equals (==) (chapter 9) · Earlier discussion of the universal equals method.
- Making destructuring declarations possible with component functions (chapter 9) · Destructuring builds on methods generated for data classes.
- Reusing property accessor logic: Delegated properties (chapter 9) · Introduced delegation as handing a task to a helper object in the context of classes.
- Introduction to lambdas: Blocks of code as values (chapter 5) · The listener example contrasts a lambda with an implementation using an object declaration.
Concepts explained in this chapter
- overrideMethod overriding is the definition of a subclass member function that replaces an inherited implementation with the same signature.
- object declarationThe object keyword is Kotlin syntax that declares a class and creates an instance of it as a single construct.
- primary constructorA primary constructor is the constructor declared in a Kotlin class header that specifies constructor parameters and can define properties initialized from them.
- superclassA base class is a class from which one or more subclasses inherit members and behavior.
- companion objectA companion object is the specially designated object declaration inside a Kotlin class whose members can be accessed through the containing class name.
- equality by valueequals is an object method that determines whether another value is equal to the receiver according to the object's equality contract.
- hash valuehashCode is an object method that returns an integer hash value consistent with the object's equality semantics.
- accessor methodA property accessor is a getter or setter that defines how a Kotlin property value is read or changed.
- secondary constructorA secondary constructor is an additional Kotlin constructor declared in a class alongside its primary constructor or as the class's constructor form.
- initializerA constructor is a Kotlin class member that initializes a new instance and receives constructor arguments.
- inheritanceInheritance is the language mechanism by which a class derives members and behavior from a superclass.
- abstract memberAn abstract method is a method declaration without an implementation that must be implemented by a concrete implementing class.
- attributeAn annotation is a declaration-associated metadata construct whose availability and retention can be configured for source processing, compiled class files, or runtime access.
- public visibilityVisibility is the access-control property that determines which code can refer to a declaration.
- finalThe final modifier prevents a Kotlin class from being subclassed or a class member from being overridden.
- initializer blockThe init keyword introduces a Kotlin initializer block in a class.
- dataclassA data class is a Kotlin class whose compiler generates value-oriented methods such as equality, hashing, string conversion, and component functions from its primary constructor properties.
- internal modifierThe internal modifier restricts a Kotlin declaration's visibility to code in the same module.
- delegationDelegation is a composition pattern in which one object forwards operations or property lookup to another object that provides the behavior.
- super keywordThe super construct accesses or invokes inherited methods, constructors, or attributes according to a language’s inheritance rules.
- inner classAn inner class is a class declared inside another class and associated with an instance of the containing class.
- nested classA nested class is a class declared inside another class; in Kotlin, it does not implicitly reference an instance of the enclosing class.
- backing fieldA backing field is the compiler-managed field that stores a Kotlin property's value when storage is required.
- object expressionAn object expression is Kotlin syntax that creates an anonymous object, optionally implementing a class or interfaces, at the point where it is written.
- inline classAn inline class is a Kotlin class with exactly one primary-constructor property whose values are represented by the underlying property type wherever possible rather than by allocated wrapper objects.
- explicit API modeExplicit API mode is a Kotlin compiler mode that requires explicitly declared public API types and related signatures.
- moduleA module is a distinct unit of Kotlin compilation whose declarations share an internal visibility boundary.
- private memberThe private modifier restricts access to a Kotlin declaration to its containing class or, for top-level declarations, its containing file.
- protected memberThe protected modifier restricts access to a declaration to its containing class and subclasses in Kotlin.
- class delegationClass delegation is a Kotlin language feature that generates interface-member implementations by forwarding them to a specified delegate object.
- default methodA default implementation is a member implementation provided by an interface that implementing types can inherit without redefining it.
- ..< operatorThe `..<` operator constructs a range that includes its lower endpoint and excludes its upper endpoint.
- constructor parameterA constructor parameter is a parameter declared by a Kotlin constructor and optionally promoted to a property.
- operatorThe `..<` operator constructs a range that includes its lower endpoint and excludes its upper endpoint.
- classA class is a Kotlin declaration that defines the structure and behavior of objects through members such as properties, functions, and constructors.
- hash code contractThe hash code contract requires equal objects to produce the same hash code.
- hashsetHashSet is a set implementation that organizes elements using hash codes for efficient membership testing.
- toStringtoString is an object method that returns a textual representation of the object.
- @JvmStatic@JvmStatic is a Kotlin annotation that causes a companion-object or object member to be exposed as a static method to Java code.
- @JvmField@JvmField is a Kotlin annotation that exposes a property as a Java field instead of only through accessors.
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.
