diff --git a/docs/topics/classes.md b/docs/topics/classes.md index 66039e7dfef..d0c0d86776f 100644 --- a/docs/topics/classes.md +++ b/docs/topics/classes.md @@ -1,263 +1,692 @@ [//]: # (title: Classes) -Classes in Kotlin are declared using the keyword `class`: +Like other object-oriented languages, Kotlin uses _classes_ to encapsulate data (properties) and behavior (functions) +for reusable, structured code. + +Classes are blueprints or templates for objects, which you +create via [constructors](#constructors-and-initializer-blocks). When you [create an instance of a class](#creating-objects-of-classes), you are creating +a concrete object based on that blueprint. + +Kotlin offers a concise syntax for declaring classes. To declare a class, use the `class` keyword +followed by the class name: ```kotlin class Person { /*...*/ } ``` -The class declaration consists of the class name, the class header (specifying its type parameters, the primary constructor, -and some other things), and the class body surrounded by curly braces. Both the header and the body are optional; if the -class has no body, the curly braces can be omitted. +The class declaration consists of: +* **Class header**, including but not limited to: + * `class` keyword + * Class name + * Type parameters (if any) + * [Primary constructor](#primary-constructor) (optional) +* **Class body** (optional), surrounded by curly braces `{}`, and including **class members** such as: + * [Secondary constructors](#secondary-constructors) + * [Initializer blocks](#initializer-blocks) + * [Functions](functions.md) + * [Properties](properties.md) + * [Nested and inner classes](nested-classes.md) + * [Object declarations](object-declarations.md) + +Here's an example of a class with header, body, and an [object created](#creating-objects-of-classes) in the `main()` function: ```kotlin -class Empty +// Class with a primary constructor that initializes the 'name' property +class Person(val name: String) { + // Class body with `age` property + var age: Int = 0 +} + +fun main() { + // Creates an object of the Person class by calling the constructor + val person = Person("Alice") + + // Access the object's properties + println(person.name) + // Alice + println(person.age) + // 0 +} ``` +{kotlin-runnable="true"} + +In this example, the `Person` class is defined with a primary constructor that declares a read-only property +`name` of type `String`. In the body, the class includes a mutable `age` property initialized to `0`. -## Constructors +This class represents a person with a fixed name and an age you can update after +object creation. An object is created +when you use the class as a blueprint to build a real thing you can work with in your program. -A class in Kotlin has a _primary constructor_ and possibly one or more _secondary constructors_. The primary constructor -is declared in the class header, and it goes after the class name and optional type parameters. +To create an object of a class, +you must provide arguments for the constructor parameters. For more details, see the section [Creating objects of classes](#creating-objects-of-classes). + +Both the class header and class body can be minimal. If the class does not have a body, you can omit the curly braces `{}`: ```kotlin -class Person constructor(firstName: String) { /*...*/ } +// Class with name and primary constructor, but without body +class Person(val name: String, var age: Int) ``` -If the primary constructor does not have any annotations or visibility modifiers, the `constructor` keyword can be omitted: +## Constructors and initializer blocks + +A class in Kotlin can have a [_primary constructor_](#primary-constructor), which is optional but commonly used; and one or more [_secondary constructors_](#secondary-constructors), +which are also optional. + +The primary constructor is the main way to initialize a class, and you have to declare it in the class header. +The secondary constructor provides additional initialization logic, and you have to declare it in the class body. + +### Primary constructor + +The primary constructor is the main way to initialize a class, +setting up the initial state of an object when [it is created](#creating-objects-of-classes). + +Let's see examples of primary constructors and objects created based on them. + +To declare a primary constructor, use the class header after the class name: ```kotlin -class Person(firstName: String) { /*...*/ } +class Person constructor(name: String) { /*...*/ } ``` -The primary constructor initializes a class instance and its properties in the class header. The class header can't contain -any runnable code. If you want to run some code during object creation, use _initializer blocks_ inside the class body. -Initializer blocks are declared with the `init` keyword followed by curly braces. Write any code that you want to run -within the curly braces. +In this example, the primary constructor initializes the class `Person` by defining a parameter `name` of type `String`. -During the initialization of an instance, the initializer blocks are executed in the same order as they appear in the -class body, interleaved with the property initializers: +If the primary constructor does not have any annotations or [visibility modifiers](visibility-modifiers.md#constructors), +you can omit the `constructor` keyword: ```kotlin -//sampleStart -class InitOrderDemo(name: String) { - val firstProperty = "First property: $name".also(::println) - - init { - println("First initializer block that prints $name") +class Person(name: String) { /*...*/ } +``` + +You can define simple properties directly in the primary constructor. To declare a read-only property, +use the `val` keyword in the parentheses before the argument name. +For a mutable property, use the `var` keyword: + +```kotlin +class Person(val name: String, var age: Int) { /*...*/ } +``` + +In this example, the `Person` class has a primary constructor with two properties: `name` and `age`. +The `name` property is declared as read-only using `val`, while the `age` property is declared as mutable using `var`, meaning that +the value of `age` can change later on. +Both properties are initialized directly when an object of `Person` [is created](#creating-objects-of-classes). + +By declaring class properties in the primary constructor using `val` or `var`, you make them accessible to +[functions](functions.md) that are members of a class: + +```kotlin +// Class with a primary constructor defining properties +class Person(val name: String, var age: Int) { + // Member function accessing class properties + fun introduce(): String { + return "Hi, I'm $name and I'm $age years old." } - - val secondProperty = "Second property: ${name.length}".also(::println) - - init { - println("Second initializer block that prints ${name.length}") +} +``` + +Let's create an object of the `Person` class. Since the class defines a primary constructor with two parameters (`name` and `age`), +you need to pass arguments for them when creating the object. + +Call the class's constructor using the class name followed by parentheses, and pass the corresponding +arguments for `name` and `age` in the parentheses: + +```kotlin +val person = Person("Alice", 30) +``` + +The previous statement creates an object of the `Person` class by calling its constructor with `"Alice"` and `30` as arguments. +Together with the class, it works like this: + +```kotlin +// Class with a primary constructor defining properties +class Person(val name: String, var age: Int) { + // Member function accessing class properties + fun introduce(): String { + return "Hi, I'm $name, and I'm $age years old." } } -//sampleEnd fun main() { - InitOrderDemo("hello") + // Creates an object of the 'Person' class + val person = Person("Alice", 25) + + // Calls the function + println(person.introduce()) + // Hi, I'm Alice, and I'm 25 years old. } ``` {kotlin-runnable="true"} -Primary constructor parameters can be used in the initializer blocks. They can also be used in property initializers -declared in the class body: +[For more details, see the section Creating objects of classes](#creating-objects-of-classes). + +Additionally, you can use a [trailing comma](coding-conventions.md#trailing-commas) when declaring many class properties: + +```kotlin +class Person( + val name: String, + val lastName: String, + var age: Int, +) { /*...*/ } +``` + +Using the previous code example, if you [create an object](#creating-objects-of-classes) of the `Person` +class by passing values for `name`, `lastName`, and `age`, you get: ```kotlin -class Customer(name: String) { - val customerKey = name.uppercase() +// Class with a primary constructor that initializes 'name', 'lastName', and 'age' +class Person( + val name: String, + val lastName: String, + var age: Int, +) + +fun main() { + // Creates an object of the 'Person' class with given values + val person = Person("John", "Doe", 30) + + // Accesses properties of the object + println("${person.name} ${person.lastName} is ${person.age} years old.") + // John Doe is 30 years old. } ``` +{kotlin-runnable="true"} + +You can also assign values to properties in the primary constructor: + +```kotlin +class Person(val name: String = "John", var age: Int = 30) { /*...*/ } +``` -Kotlin has a concise syntax for declaring properties and initializing them from the primary constructor: +Assigning values in primary constructors ensures that if no values are passed during [object creation](#creating-objects-of-classes), the property uses the default values: ```kotlin -class Person(val firstName: String, val lastName: String, var age: Int) +// Class with a primary constructor including default values for 'name' and 'age' +class Person(val name: String = "John", var age: Int = 30) + +fun main() { + // Creates an object using default values + val person = Person() + println("Name: ${person.name}, Age: ${person.age}") + // Name: John, Age: 30 +} ``` +{kotlin-runnable="true"} -Such declarations can also include default values of the class properties: +Also, you can use the primary constructor parameters to initialize additional class properties directly in the class body: ```kotlin -class Person(val firstName: String, val lastName: String, var isEmployed: Boolean = true) +// Class with a primary constructor including values for 'name' and 'age' +class Person(val name: String = "John", var age: Int = 30) { + // Property 'description' initialized using the primary constructor + val description: String = "Name: $name, Age: $age" +} ``` -You can use a [trailing comma](coding-conventions.md#trailing-commas) when you declare class properties: +In this example, the primary constructor `(val name: String = "John", var age: Int = 30)` initializes the class's properties +and sets their initial values. Then, `description` is an additional string property, +initialized in the class body by accessing the class properties. + +Here's the same example with an [object created](#creating-objects-of-classes): ```kotlin +// Class with a primary constructor including values for 'name' and 'age' class Person( - val firstName: String, - val lastName: String, - var age: Int, // trailing comma -) { /*...*/ } + val name: String = "John", + var age: Int = 30 +) { + // Property 'description' initialized using the primary constructor + val description: String = "Name: $name, Age: $age" +} + +fun main() { + // Create an instance of the 'Person' class + val person = Person() + // Accesses the 'description' property from the 'person' object + println(person.description) + // Name: John, Age: 30 +} ``` +{kotlin-runnable="true"} + +### Initializer blocks -Much like regular properties, properties declared in the primary constructor can be mutable (`var`) or read-only (`val`). +The primary constructor can't contain runnable code. Its purpose is to initialize classes and set class +properties. For any logic or behavior that requires actual code execution, you need to place it somewhere else. -Plain constructor parameters (that are not properties) are accessible in: -* The class header. -* Initialized properties within the class body. -* Initializer blocks. +If you want to run some code during [object creation](#creating-objects-of-classes), use _initializer blocks_ inside the class body. -For example: +Declare initializer blocks with the `init` keyword followed by curly braces `{}`. Write any code that you want to run +within the curly braces: ```kotlin -// width and height are plain constructor parameters -class RectangleWithParameters(width: Int, height: Int) { - val perimeter = 2 * width + 2 * height +// Class with a primary constructor that initializes 'name' and 'age' +class Person(val name: String, var age: Int) { + init { + // Initializer block executed when an object is created + println("Person created: $name, age $age") + } +} +``` + +In the example above, after the class declaration, there's an initializer block (`init {}`) containing runnable code +to print a message that uses the class properties. + +Considering the same code example, if you [create an object](#creating-objects-of-classes) of the `Person` class +by passing values for `name` and `age`, the output is: +```kotlin +//sampleStart +// Class with a primary constructor that initializes 'name' and 'age' +class Person(val name: String, var age: Int) { init { - println("Rectangle created with width = $width and height = $height") + // Initializer block executed when an object is created + println("Person created: $name, age $age.") + // Person created: John, age 30. } } + +fun main() { + // Creates an object of the 'Person' class + Person("John", 30) +} +//sampleEnd ``` +{kotlin-runnable="true"} + +You can add as many initializer blocks (`init {}`) as you need, and they will be executed in the order in which they appear in the class body, +interleaved with property initializers. -If the constructor has annotations or visibility modifiers, the `constructor` keyword is required and the modifiers go before it: +Here's an example of a class with two initializer blocks containing logic that runs when the [object is created](#creating-objects-of-classes). In this case, +the object of the `Person` class is created by passing values for `name` and `age`: ```kotlin -class Customer public @Inject constructor(name: String) { /*...*/ } +//sampleStart +// Class with a primary constructor that initializes 'name' and 'age' +class Person(val name: String, var age: Int) { + // First initializer block + init { + // Runs first when an object is created + println("Person created: $name, age $age.") + // Person created: John, age 30. + } + + // Second initializer block + init { + // Runs after the first initializer block + if (age < 18) { + println("$name is a minor.") + } else { + println("$name is an adult.") + // John is an adult. + } + } +} + +fun main() { + // Creates an object of the 'Person' class + Person("John", 30) +} +//sampleEnd ``` +{kotlin-runnable="true"} -Learn more about [visibility modifiers](visibility-modifiers.md#constructors). +You can use the primary constructor parameters in the initializer blocks. For example, in the code above, the first and second initializers use +the `name` and `age` parameters from the primary constructor. ### Secondary constructors -A class can also declare _secondary constructors_, which are prefixed with `constructor`: +In Kotlin, a secondary constructor is an additional constructor that a class can have beyond its primary constructor. +Secondary constructors allow you to provide different ways to initialize an object when the primary constructor alone +isn't enough. + +Secondary constructors are useful for [Java interoperability](java-to-kotlin-interop.md) and when +you need multiple ways to initialize a class. + +To declare a secondary constructor, use the `constructor` +keyword inside the class body. After the keyword, add the constructor parameters within parenthesis. +Then, use curly braces with the constructor logic inside: ```kotlin -class Person(val pets: MutableList = mutableListOf()) +// Class header with primary constructor that initializes 'name' and 'age' +class Person(val name: String, var age: Int) { + // Class body with secondary constructor + constructor(name: String) : this(name, 0) { /*...*/ } +} +``` + +See secondary constructors in action. The following code snippet [creates an object](#creating-objects-of-classes) +`Person("Bob")` based on the secondary constructor. The secondary constructor assigns a default age (`8`) and takes the +`name` parameter. The constructor logic prints a message when the secondary constructor is called: -class Pet { - constructor(owner: Person) { - owner.pets.add(this) // adds this pet to the list of its owner's pets +```kotlin +// Class header with primary constructor that initializes 'name' and 'age' +class Person(val name: String, var age: Int) { + // Secondary constructor that takes a String age and converts it + constructor(name: String, age: String) : this(name, age.toIntOrNull() ?: 0) { + println("$name created with converted age: $age") + // Bob created with converted age: 8 } } + +fun main() { + // Uses the secondary constructor with a String age + Person("Bob", "8") +} ``` +{kotlin-runnable="true"} + +In the code above, the secondary constructor calls or delegates to the primary constructor via the [`this` keyword](this-expressions.md), +passing `name` and the default value of `age`. + +In Kotlin, secondary constructors must delegate to the primary constructor. This delegation ensures that all primary +constructor initialization logic is executed before any secondary constructor logic runs. -If the class has a primary constructor, each secondary constructor needs to delegate to the primary constructor, either -directly or indirectly through another secondary constructor(s). Delegation to another constructor of the same class is -done using the `this` keyword: +Constructor delegation can be: +* **Direct**, where the secondary constructor calls the primary constructor immediately. +* **Indirect**, where one secondary constructor calls another, which in turn delegates to the primary constructor. + +Here's an example demonstrating how direct and indirect delegation works: ```kotlin -class Person(val name: String) { - val children: MutableList = mutableListOf() - constructor(name: String, parent: Person) : this(name) { - parent.children.add(this) +// Class header with primary constructor that initializes 'name' and 'age' +class Person(val name: String, var age: Int) { + // Secondary constructor with direct delegation to the primary constructor + constructor(name: String) : this(name, 0) { + println("Person created with default name: $name") + } + + // Secondary constructor with indirect delegation: 'this("Bob")' -> 'constructor(name: String)' -> primary constructor + constructor() : this("Bob") { + println("New person created with default name: $name") } } ``` -Code in initializer blocks effectively becomes part of the primary constructor. Delegation to the primary constructor -happens at the moment of access to the first statement of a secondary constructor, so the code in all initializer blocks -and property initializers is executed before the body of the secondary constructor. +The same code example with two [objects created](#creating-objects-of-classes), one for the direct delegation and the other for +the indirect delegation, looks like this: + +```kotlin +// Class header with primary constructor that initializes 'name' and 'age' +class Person( + val name: String, + var age: Int +) { + // Secondary constructor with direct delegation to the primary constructor + constructor(name: String) : this(name, 0) { + println("Person created with default age: $age and name: $name.") + // Person created with default age: 0 and name: Alice. + // Person created with default age: 0 and name: Bob. + } + // Secondary constructor with indirect delegation: 'this("Bob")' -> 'constructor(name: String)' -> primary constructor + constructor() : this("Bob") { + println("New person created with default age: $age and name: $name.") + // New person created with default age: 0 and name: Bob. + } +} + +fun main() { + // Creates an object based on the direct delegation + Person("Alice") + + // Creates an object based on the indirect delegation + Person() +} +``` +{kotlin-runnable="true"} + +In classes with initializer blocks (`init {}`), the code within these blocks becomes part of the primary constructor. +When a secondary constructor is invoked, it delegates to the primary constructor first. As a result, all initializer blocks +and property initializers run before the body of the secondary constructor. + +Whenever an [object of a class is created](#creating-objects-of-classes), all initializer blocks run before any secondary constructor logic. Even if the class has no primary constructor, the delegation still happens implicitly, and the initializer blocks are still executed: ```kotlin //sampleStart -class Constructors { +// Class header with no primary constructor +class Person { + // Initializer block executed when an object is created init { - println("Init block") + // Runs before the secondary constructor + println("1. First initializer block runs") + // 1. First initializer block runs } + // Secondary constructor that takes an integer parameter constructor(i: Int) { - println("Constructor $i") + // Runs after the initializer block + println("2. Person $i is created") + // 2. Person 1 created } } -//sampleEnd fun main() { - Constructors(1) + // Creates an object of the 'Person' class using the secondary constructor + Person(1) } +//sampleEnd ``` {kotlin-runnable="true"} -If a non-abstract class does not declare any constructors (primary or secondary), it will have a generated primary constructor -with no arguments. The visibility of the constructor will be public. +When the `Person(1)` object is created, the initializer block (`init {}`) executes first, followed by the secondary constructor's print statement. + +If there's a class that is a [non-abstract class](#abstract-classes) and does not declare any constructors (primary or secondary), it will have an implicit primary constructor +with no arguments: +```kotlin +// Class with no explicit constructors +class Person { + // No primary or secondary constructors declared +} + +fun main() { + // Creates an object of the 'Person' class using the implicit primary constructor + val person = Person() +} +``` + +The visibility of the constructor will be public, meaning it can be accessed from anywhere. If you don't want your class to have a public constructor, declare an empty primary constructor with non-default visibility: ```kotlin -class DontCreateMe private constructor() { /*...*/ } +class Person private constructor() { /*...*/ } ``` -> On the JVM, if all of the primary constructor parameters have default values, the compiler will generate an additional parameterless constructor which will use the default values. This makes it easier to use Kotlin with libraries such as Jackson or JPA that create class instances through parameterless constructors. +> On the JVM, if all primary constructor parameters have default values, the compiler implicitly provides +> a parameterless constructor that uses those default values. +> +> This makes it easier to use Kotlin with libraries such as [Jackson](https://github.com/FasterXML/jackson) +> or [Spring Data JPA](https://spring.io/projects/spring-data-jpa), which create class objects through +> parameterless constructors. > +> In the following example, Kotlin implicitly provides a parameterless constructor `Person()` that uses the default value `""`: +> > ```kotlin -> class Customer(val customerName: String = "") +> class Person(val personName: String = "") > ``` > {style="note"} -## Creating instances of classes +## Creating objects of classes + +Like in other object-oriented programming languages, a class in Kotlin serves as a blueprint or template +that defines the properties (attributes) and behaviors (functions) of objects. When you create an instance +of a class, you are creating a concrete object based on that blueprint. + +To create an object of a class, use the class name followed by parentheses (`()`), similar to calling a function: + +```kotlin +// Creates an object of the 'Person' class +val person1 = Person() +``` + +In Kotlin, you can create objects: + +* **Without arguments** (`()`): creates an object using a constructor with default values. +* **With arguments** (`("value")`): creates an object by passing specific values to the constructor. -To create an instance of a class, call the constructor as if it were a regular function. You can assign the created instance to a [variable](basic-syntax.md#variables): +As you can see, you can assign the created object to a mutable (`var`) or immutable (`val`) [variable](basic-syntax.md#variables): ```kotlin -val invoice = Invoice() +// Creates an object by passing a specific value and assigns it to a mutable variable +var person2 = Person("Joe") -val customer = Customer("Joe Smith") +// Creates an object using the default constructor's value and assigns it to an immutable variable +val person1 = Person() ``` -> Kotlin does not have a `new` keyword. -> -{style="note"} +You can create objects wherever you need them, inside the `main()` function, within other functions, or as part of a class. + +When creating objects inside the `main()` function, when the program runs, execution starts from `main()`. +Here is where you instantiate objects to interact with them. When creating objects of a class, you're invoking constructors +(for example, `Person()` or `Person("Joe")`) directly inside the `main()` function. -The process of creating instances of nested, inner, and anonymous inner classes is described in [Nested classes](nested-classes.md). +Let's look at an example of how object creation works. + +The following code defines a `Person` class with a property for storing a name. +When an object of the class is created, an initialization block runs to print a message. The example demonstrates +creating an object with both the default constructor's value and a specific value: + +```kotlin +// Class header with primary constructor that initializes 'name' with a default value +class Person(val name: String = "Sebastian") + +fun main() { + // Creates an object using the default constructor's value + val person1 = Person() + println("Person object created: ${person1.name}") + // Person object created: Sebastian + + // Creates an object by passing a specific value + val person2 = Person("Joe") + println("Person object created: ${person2.name}") + // Person object created: Joe +} +``` +{kotlin-runnable="true"} -## Class members +> In Kotlin, unlike other object-oriented programming languages like Java, +> there is no need for the `new` keyword when creating objects of a class. +> +{style="note"} -Classes can contain: +Additionally, you can create objects inside another function and call that function from `main()`. -* [Constructors and initializer blocks](#constructors) -* [Functions](functions.md) -* [Properties](properties.md) -* [Nested and inner classes](nested-classes.md) -* [Object declarations](object-declarations.md) +For information about creating objects of nested, inner, and anonymous inner classes, +see the [Nested classes](nested-classes.md) section. ## Inheritance -Classes can be derived from each other and form inheritance hierarchies. -[Learn more about inheritance in Kotlin](inheritance.md). +Class inheritance in Kotlin allows you to create a new class (derived class) from an existing class (base class), +inheriting its properties and functions while adding or modifying behavior. + +> For detailed information about inheritance hierarchies and the use of the `open` keyword, see the [Inheritance](inheritance.md) section. +> +{style="note"} ## Abstract classes -A class may be declared `abstract`, along with some or all of its members. -An abstract member does not have an implementation in its class. -You don't need to annotate abstract classes or functions with `open`. +An abstract class in Kotlin is a class that cannot be instantiated directly and is designed to be inherited +by other classes. + +An abstract class can define abstract properties and functions, which must be implemented +by subclasses. + +You can declare an abstract class using the `abstract` modifier: ```kotlin -abstract class Polygon { - abstract fun draw() -} +abstract class Person +``` -class Rectangle : Polygon() { - override fun draw() { - // draw the rectangle - } -} +Like any other class, abstract classes can also have constructors. +These constructors initialize class properties and enforce required parameters for subclasses: + +```kotlin +abstract class Person(val name: String, val age: Int) ``` -You can override a non-abstract `open` member with an abstract one. +An abstract class can have both abstract and non-abstract members (properties and functions). +To declare a member as abstract, you must use the `abstract` keyword explicitly. + +Abstract members do not have an implementation in the abstract class. The `override` +keyword ensures that a subclass provides an actual implementation for an abstract function: ```kotlin -open class Polygon { - open fun draw() { - // some default polygon drawing method +// Abstract class with a primary constructor that defines 'name' and 'age' +abstract class Person( + val name: String, + val age: Int +) { + // Abstract member (doesn't provide default implementation, must be implemented by subclasses) + abstract fun introduce() + + // Non-abstract member (has a default implementation) + fun greet() { + println("Hello, my name is $name.") + // Hello, my name is Alice. + } +} + +// Subclass that provides an implementation for the abstract member +class Student( + name: String, + age: Int, + val school: String +) : Person(name, age) { + override fun introduce() { + println("I am $name, $age years old, and I study at $school.") + // I am Alice, 20 years old, and I study at Engineering University. } } -abstract class WildShape : Polygon() { - // Classes that inherit WildShape need to provide their own - // draw method instead of using the default on Polygon - abstract override fun draw() +fun main() { + // Creates an object of the Student class + val student = Student("Alice", 20, "Engineering University") + // Calls the non-abstract member + student.greet() + // Calls the overridden abstract member + student.introduce() } ``` +{kotlin-runnable="true"} + +You don't need to annotate abstract classes or functions with the `open` keyword because they are implicitly +open and designed to be inherited. + +[For more details about the `open` keyword, see Inheritance](inheritance.md#open-keyword). ## Companion objects -If you need to write a function that can be called without having a class instance but that needs access to the internals -of a class (such as a factory method), you can write it as a member of an [object declaration](object-declarations.md) inside that class. +In Kotlin, a [companion object](object-declarations.md#companion-objects) is a type of object declaration +that allows you to access its members using the class name without needing an object from the class. + +If you need to write a function that can be called without creating an object of a class but still needs access to the +class's companion object members, you can define it inside an [object declaration](object-declarations.md) within the class: -Even more specifically, if you declare a [companion object](object-declarations.md#companion-objects) inside your class, +```kotlin +// Class with a primary constructor that defines the 'name' property +class Person( + val name: String +) { + // Class body with a companion object + companion object { + fun createAnonymous() = Person("Anonymous") + } +} + +fun main() { + // Calls the function without creating an object of the class + val anonymous = Person.createAnonymous() + println(anonymous.name) + // Anonymous +} +``` +{kotlin-runnable="true"} + +If you declare `companion object` inside your class, you can access its members using only the class name as a qualifier. + +> [See further details about companion objects here](object-declarations.md#companion-objects). +> +{style="note"} \ No newline at end of file diff --git a/docs/topics/collection-operations.md b/docs/topics/collection-operations.md index 645ed144958..92d8aeca97d 100644 --- a/docs/topics/collection-operations.md +++ b/docs/topics/collection-operations.md @@ -6,7 +6,7 @@ transformations, and so on. ## Extension and member functions -Collection operations are declared in the standard library in two ways: [member functions](classes.md#class-members) of +Collection operations are declared in the standard library in two ways: [member functions](classes.md) of collection interfaces and [extension functions](extensions.md#extension-functions). Member functions define operations that are essential for a collection type. For example, [`Collection`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-collection/index.html) diff --git a/docs/topics/data-classes.md b/docs/topics/data-classes.md index 61289bd7a51..50bd87bed6a 100644 --- a/docs/topics/data-classes.md +++ b/docs/topics/data-classes.md @@ -34,7 +34,7 @@ Additionally, the generation of data class members follows these rules with rega Data classes may extend other classes (see [Sealed classes](sealed-classes.md) for examples). > On the JVM, if the generated class needs to have a parameterless constructor, default values for the properties have -> to be specified (see [Constructors](classes.md#constructors)): +> to be specified (see [Constructors](classes.md#constructors-and-initializer-blocks)): > > ```kotlin > data class User(val name: String = "", val age: Int = 0) diff --git a/docs/topics/exceptions.md b/docs/topics/exceptions.md index 42749c24abd..8be5c3343c0 100644 --- a/docs/topics/exceptions.md +++ b/docs/topics/exceptions.md @@ -24,7 +24,7 @@ class`](inheritance.md), you can create [custom exceptions](#create-custom-excep You can manually throw exceptions with the `throw` keyword. Throwing an exception indicates that an unexpected runtime error has occurred in the code. -Exceptions are [objects](classes.md#creating-instances-of-classes), and throwing one creates an instance of an exception class. +Exceptions are [objects](classes.md#creating-objects-of-classes), and throwing one creates an instance of an exception class. You can throw an exception without any parameters: diff --git a/docs/topics/inheritance.md b/docs/topics/inheritance.md index 679591cf5fa..4c7604735a6 100644 --- a/docs/topics/inheritance.md +++ b/docs/topics/inheritance.md @@ -15,6 +15,8 @@ open class Base // Class is open for inheritance ``` +[For more information, see Open keyword](#open-keyword). + To declare an explicit supertype, place the type after a colon in the class header: ```kotlin @@ -38,6 +40,59 @@ class MyView : View { } ``` +## Open keyword + +In Kotlin, the `open` keyword indicates that a class or a member (function or property) can be overridden in subclasses. +By default, Kotlin classes and their members are _final_, meaning they cannot be inherited or overridden unless explicitly marked as `open`: + +```kotlin +// Base class with the 'open' keyword to allow inheritance +open class Person( + val name: String +) { + // Open function that can be overridden in a subclass + open fun introduce() { + println("Hello, my name is $name.") + } +} + +// Subclass inheriting from 'Person' and overriding the 'introduce()' function +class Student( + name: String, + val school: String +) : Person(name) { + override fun introduce() { + println("Hi, I'm $name, and I study at $school.") + } +} +``` + +In addition, you need to add the `open` keyword to every property +or function that can be overridden. + +If you override a member of a base class, the overriding member +will also be open by default. If you want to change this and forbid the subclasses of your +class from overriding your implementation, you can explicitly mark the overriding +member as `final`: + +```kotlin +// Base class with the 'open' keyword to allow inheritance +open class Person(val name: String) { + // Open function that can be overridden in a subclass + open fun introduce() { + println("Hello, my name is $name.") + } +} + +// Subclass inheriting from 'Person' and overriding the 'introduce()' function +class Student(name: String, val school: String) : Person(name) { + // The 'final' keyword prevents further overriding in subclasses + final override fun introduce() { + println("Hi, I'm $name, and I study at $school.") + } +} +``` + ## Overriding methods Kotlin requires explicit modifiers for overridable members and overrides: diff --git a/docs/topics/keyword-reference.md b/docs/topics/keyword-reference.md index 1f775effc22..103ef5ad591 100644 --- a/docs/topics/keyword-reference.md +++ b/docs/topics/keyword-reference.md @@ -43,7 +43,7 @@ The following tokens are always interpreted as keywords and cannot be used as id - [calls the superclass constructor from a secondary constructor](classes.md#inheritance). * `this` - refers to [the current receiver](this-expressions.md). - - [calls another constructor of the same class from a secondary constructor](classes.md#constructors). + - [calls another constructor of the same class from a secondary constructor](classes.md#constructors-and-initializer-blocks). * `throw` [throws an exception](exceptions.md). * `true` specifies the 'true' value of the [Boolean type](booleans.md). * `try` [begins an exception-handling block](exceptions.md). @@ -63,7 +63,7 @@ as identifiers in other contexts: - [delegates the implementation of an interface to another object](delegation.md). - [delegates the implementation of the accessors for a property to another object](delegated-properties.md). * `catch` begins a block that [handles a specific exception type](exceptions.md). - * `constructor` declares a [primary or secondary constructor](classes.md#constructors). + * `constructor` declares a [primary or secondary constructor](classes.md#constructors-and-initializer-blocks). * `delegate` is used as an [annotation use-site target](annotations.md#annotation-use-site-targets). * `dynamic` references a [dynamic type](dynamic-type.md) in Kotlin/JS code. * `field` is used as an [annotation use-site target](annotations.md#annotation-use-site-targets). @@ -73,7 +73,7 @@ as identifiers in other contexts: - declares the [getter of a property](properties.md#getters-and-setters). - is used as an [annotation use-site target](annotations.md#annotation-use-site-targets). * `import` [imports a declaration from another package into the current file](packages.md). - * `init` begins an [initializer block](classes.md#constructors). + * `init` begins an [initializer block](classes.md#constructors-and-initializer-blocks). * `param` is used as an [annotation use-site target](annotations.md#annotation-use-site-targets). * `property` is used as an [annotation use-site target](annotations.md#annotation-use-site-targets). * `receiver` is used as an [annotation use-site target](annotations.md#annotation-use-site-targets). diff --git a/docs/topics/tour/kotlin-tour-classes.md b/docs/topics/tour/kotlin-tour-classes.md index 4b08dfb3b20..80e6ef18ef5 100644 --- a/docs/topics/tour/kotlin-tour-classes.md +++ b/docs/topics/tour/kotlin-tour-classes.md @@ -80,7 +80,7 @@ In the example: * `id` and `email` are used with the default constructor to create `contact`. Kotlin classes can have many constructors, including ones that you define yourself. To learn more about how to declare -multiple constructors, see [Constructors](classes.md#constructors). +multiple constructors, see [Constructors](classes.md#constructors-and-initializer-blocks). ## Access properties