From 977b6e30b3d2fa819b2c853515fece13e3306d04 Mon Sep 17 00:00:00 2001 From: alepedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Wed, 2 Apr 2025 16:06:43 +0200 Subject: [PATCH 1/8] update: improve Classes page --- docs/topics/classes.md | 718 ++++++++++++++++++++++++++++++++++------- 1 file changed, 601 insertions(+), 117 deletions(-) diff --git a/docs/topics/classes.md b/docs/topics/classes.md index 66039e7dfef..0e6df38e674 100644 --- a/docs/topics/classes.md +++ b/docs/topics/classes.md @@ -1,263 +1,747 @@ [//]: # (title: Classes) -Classes in Kotlin are declared using the keyword `class`: +Like other object-oriented languages, Kotlin uses _classes_ to encapsulate data and behavior +for reusable, structured code. + +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: + * `class` keyword + * Class name + * Type parameters (if any) + * [Primary constructor](#primary-constructor) (optional) +* **Class body** (optional), surrounded by curly braces `{}`, and including class members: + * [Secondary constructors](#secondary-constructors) + * [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 declaration with a property: ```kotlin -class Empty +// Class header with a primary constructor that initializes the 'name' property +class Person(val name: String) { + // Class body with the `age` property + var age: Int = 0 +} ``` -## Constructors +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`. +This class represents a person with a fixed name and an age you can update after +[object creation](#creating-objects-of-classes). -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. +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 only a name and no body +class Person ``` -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 has a [_primary constructor_](#primary-constructor) and, optionally, one or more [_secondary constructors_](#secondary-constructors). + +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). + +Declare the primary constructor in 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`. + +This primary constructor ensures that when [creating an object](#creating-objects-of-classes) of `Person`, a +value for `name` must be provided, defining an initial required input. -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. This approach makes it easy to create +classes that need properties initialized directly during object creation. + +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). + +A class can define member [functions](functions.md) that, in turn, access class properties. +To make a primary constructor parameter accessible in member functions, declare it as a property using `val` or `var`: + +```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}") +} +``` + +See classes in action. 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 +// 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: +Additionally, you can use a [trailing comma](coding-conventions.md#trailing-commas) when declaring many class properties: ```kotlin -class Customer(name: String) { - val customerKey = name.uppercase() +class Person( + val name: String, + val lastName: String, + var age: Int, +) { /*...*/ } +``` + +Using the trailing comma 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 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 using the constructor parameters. + +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 as you need, and they will be executed in the order in which they appear in the class body, +interleaved with property initializers (`init{}`). -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 when interacting with Java code that requires traditional constructors or if +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 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: + +```kotlin +// Class header with primary constructor that initializes 'name' and 'age' +class Person(val name: String, var age: Int) { + // Secondary constructor + constructor(name: String) : this(name, 8) { + println("$name created with default age: $age") + // Bob created with default age: 8 + } +} + +fun main() { + // Creates an object using the secondary constructor, which sets 'age' to 8 + Person("Bob") +} +``` +{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. + +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. + +> For further information about delegation via superclass constructor, see [Calling the superclass implementation](inheritance.md#calling-the-superclass-implementation). +> +{style="note"} + +Here's an example demonstrating how direct and indirect delegation works: ```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) { + // Secondary constructor with direct delegation to the primary constructor + constructor(name: String) : this(name, 0) { + println("Person created with default name: $name") + } -class Pet { - constructor(owner: Person) { - owner.pets.add(this) // adds this pet to the list of its owner's pets + // Secondary constructor with indirect delegation: 'this("Bob")' -> 'constructor(name: String)' -> primary constructor + constructor() : this("Bob") { + println("New person created with default name: $name") } } ``` -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: +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 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 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"} -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. +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("Initializer block executed") + // Initializer block executed } + // Secondary constructor that takes an integer parameter constructor(i: Int) { - println("Constructor $i") + // Runs after the initializer block + println("Person $i is created") + // 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 a generated 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 automatically generated 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 generates +> an additional 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 automatically generates 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 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): +To create an object of a class, use the class name followed by parentheses (`()`), similar to calling a function: ```kotlin -val invoice = Invoice() +// Creates an object of the 'Person' class +val person1 = Person() +``` + +In Kotlin, you can create objects: -val customer = Customer("Joe Smith") +* **Without arguments** (`()`): creates an object using a constructor with default values. +* **With arguments** (`("value")`): creates an object by passing specific values to the constructor. + +As you can see, you can assign the created object to a mutable (`var`) or immutable (`val`) [variable](basic-syntax.md#variables): + +```kotlin +// Creates an object by passing a specific value and assigns it to a mutable variable +var person2 = Person("Joe") + +// 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"} +Typically, you create objects within the `main()` function because it serves as the entry point of your Kotlin program. -The process of creating instances of nested, inner, and anonymous inner classes is described in [Nested classes](nested-classes.md). +When the program runs, execution starts from the `main()` function, so this 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. -## Class members +Let's look at an example of how object creation works. -Classes can contain: +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: -* [Constructors and initializer blocks](#constructors) -* [Functions](functions.md) -* [Properties](properties.md) -* [Nested and inner classes](nested-classes.md) -* [Object declarations](object-declarations.md) +```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"} + +> 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"} + +Additionally, you can create objects inside another function and call that function from `main()`. + +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 +``` + +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) +``` + +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 +// 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. + } } -class Rectangle : Polygon() { - override fun draw() { - // draw the rectangle +// 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. } } + +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. + +Similarly, you can override a non-abstract `open` member with an abstract one: + +## Open keyword -You can override a non-abstract `open` member with an abstract one. +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 -open class Polygon { - open fun draw() { - // some default polygon drawing method +// 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.") } } -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() +// 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.") + } +} +``` + +You don't need to annotate abstract classes or functions with the `open` keyword because they are implicitly +open and designed to be inherited. + +Similarly, you can override a non-abstract `open` member with an abstract one: + +```kotlin +// Abstract class with a primary constructor that defines 'name' +abstract class Person(val name: String) { + // Non-abstract open member that allows overriding + open fun greet() { + println("Hello, my name is $name.") + } + + // Abstract member + abstract fun introduce() +} + +// Subclass that overrides the 'greet()' function with an abstract one +abstract class Student(name: String, val school: String) : Person(name) { + // Overrides the non-abstract 'greet()' function with an abstract one + abstract override fun greet() + + // Overrides the abstract 'introduce()' function with an abstract one + override fun introduce() { + println("I am $name, and I study at $school.") + } +} + +``` + ## 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 having a class object but still needs access +to the internals of the class, you can define it inside `companion object` within that 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 From b5716b27f7b4c3594fddad04f5578bc3acfdab8e Mon Sep 17 00:00:00 2001 From: alepedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Mon, 7 Apr 2025 11:22:21 +0200 Subject: [PATCH 2/8] chore: fix anchors --- docs/topics/collection-operations.md | 2 +- docs/topics/data-classes.md | 2 +- docs/topics/exceptions.md | 2 +- docs/topics/keyword-reference.md | 6 +++--- docs/topics/tour/kotlin-tour-classes.md | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) 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 e40bbb5338d..3ecfbcb18f8 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/keyword-reference.md b/docs/topics/keyword-reference.md index 907eb0cf037..6983ffd8ec9 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). - 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 From 7ebdb3dfc9b8557b4cdc26aae4afac257d1ed472 Mon Sep 17 00:00:00 2001 From: alepedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Wed, 23 Apr 2025 14:35:39 +0200 Subject: [PATCH 3/8] Marat review --- docs/topics/classes.md | 221 ++++++++++++++----------------------- docs/topics/inheritance.md | 55 +++++++++ 2 files changed, 138 insertions(+), 138 deletions(-) diff --git a/docs/topics/classes.md b/docs/topics/classes.md index 0e6df38e674..d0c0d86776f 100644 --- a/docs/topics/classes.md +++ b/docs/topics/classes.md @@ -1,8 +1,12 @@ [//]: # (title: Classes) -Like other object-oriented languages, Kotlin uses _classes_ to encapsulate data and behavior +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: @@ -11,43 +15,62 @@ class Person { /*...*/ } ``` The class declaration consists of: -* **Class header**, including: +* **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: +* **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 declaration with a property: +Here's an example of a class with header, body, and an [object created](#creating-objects-of-classes) in the `main()` function: ```kotlin -// Class header with a primary constructor that initializes the 'name' property +// Class with a primary constructor that initializes the 'name' property class Person(val name: String) { - // Class body with the `age` property + // 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`. + This class represents a person with a fixed name and an age you can update after -[object creation](#creating-objects-of-classes). +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. + +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 with only a name and no body -class Person +// Class with name and primary constructor, but without body +class Person(val name: String, var age: Int) ``` ## Constructors and initializer blocks -A class in Kotlin has a [_primary constructor_](#primary-constructor) and, optionally, one or more [_secondary constructors_](#secondary-constructors). +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. @@ -55,9 +78,11 @@ The secondary constructor provides additional initialization logic, and you have ### 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). +setting up the initial state of an object when [it is created](#creating-objects-of-classes). -Declare the primary constructor in the class header after the class name: +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 constructor(name: String) { /*...*/ } @@ -65,9 +90,6 @@ class Person constructor(name: String) { /*...*/ } In this example, the primary constructor initializes the class `Person` by defining a parameter `name` of type `String`. -This primary constructor ensures that when [creating an object](#creating-objects-of-classes) of `Person`, a -value for `name` must be provided, defining an initial required input. - If the primary constructor does not have any annotations or [visibility modifiers](visibility-modifiers.md#constructors), you can omit the `constructor` keyword: @@ -75,10 +97,8 @@ you can omit the `constructor` keyword: class Person(name: String) { /*...*/ } ``` -You can define simple properties directly in the primary constructor. This approach makes it easy to create -classes that need properties initialized directly during object creation. - -To declare a read-only property, use the `val` keyword in the parentheses before the argument name. +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 @@ -90,8 +110,8 @@ The `name` property is declared as read-only using `val`, while the `age` proper the value of `age` can change later on. Both properties are initialized directly when an object of `Person` [is created](#creating-objects-of-classes). -A class can define member [functions](functions.md) that, in turn, access class properties. -To make a primary constructor parameter accessible in member functions, declare it as a property using `val` or `var`: +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 @@ -103,8 +123,18 @@ class Person(val name: String, var age: Int) { } ``` -See classes in action. 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: +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 @@ -126,6 +156,8 @@ fun main() { ``` {kotlin-runnable="true"} +[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 @@ -136,7 +168,7 @@ class Person( ) { /*...*/ } ``` -Using the trailing comma example, if you [create an object](#creating-objects-of-classes) of the `Person` +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 @@ -191,7 +223,7 @@ class Person(val name: String = "John", var age: Int = 30) { 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 using the constructor parameters. +initialized in the class body by accessing the class properties. Here's the same example with an [object created](#creating-objects-of-classes): @@ -235,7 +267,7 @@ class Person(val name: String, var age: Int) { } ``` -In the example above, after the class declaration, there's an initializer block (`init{}`) containing runnable code +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 @@ -260,8 +292,8 @@ fun main() { ``` {kotlin-runnable="true"} -You can add as many initializer blocks as you need, and they will be executed in the order in which they appear in the class body, -interleaved with property initializers (`init{}`). +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. 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`: @@ -306,7 +338,7 @@ In Kotlin, a secondary constructor is an additional constructor that a class can Secondary constructors allow you to provide different ways to initialize an object when the primary constructor alone isn't enough. -Secondary constructors are useful when interacting with Java code that requires traditional constructors or if +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` @@ -328,16 +360,16 @@ See secondary constructors in action. The following code snippet [creates an obj ```kotlin // Class header with primary constructor that initializes 'name' and 'age' class Person(val name: String, var age: Int) { - // Secondary constructor - constructor(name: String) : this(name, 8) { - println("$name created with default age: $age") - // Bob created with default age: 8 + // 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() { - // Creates an object using the secondary constructor, which sets 'age' to 8 - Person("Bob") + // Uses the secondary constructor with a String age + Person("Bob", "8") } ``` {kotlin-runnable="true"} @@ -352,10 +384,6 @@ 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. -> For further information about delegation via superclass constructor, see [Calling the superclass implementation](inheritance.md#calling-the-superclass-implementation). -> -{style="note"} - Here's an example demonstrating how direct and indirect delegation works: ```kotlin @@ -421,15 +449,15 @@ class Person { // Initializer block executed when an object is created init { // Runs before the secondary constructor - println("Initializer block executed") - // Initializer block executed + println("1. First initializer block runs") + // 1. First initializer block runs } // Secondary constructor that takes an integer parameter constructor(i: Int) { // Runs after the initializer block - println("Person $i is created") - // Person 1 created + println("2. Person $i is created") + // 2. Person 1 created } } @@ -443,7 +471,7 @@ fun main() { 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 a generated primary constructor +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 @@ -453,7 +481,7 @@ class Person { } fun main() { - // Creates an object of the 'Person' class using the automatically generated primary constructor + // Creates an object of the 'Person' class using the implicit primary constructor val person = Person() } ``` @@ -465,14 +493,14 @@ If you don't want your class to have a public constructor, declare an empty prim class Person private constructor() { /*...*/ } ``` -> On the JVM, if all primary constructor parameters have default values, the compiler generates -> an additional parameterless constructor that uses those default values. +> 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 automatically generates a parameterless constructor `Person()` that uses the default value `""`: +> In the following example, Kotlin implicitly provides a parameterless constructor `Person()` that uses the default value `""`: > > ```kotlin > class Person(val personName: String = "") @@ -508,11 +536,11 @@ var person2 = Person("Joe") val person1 = Person() ``` -Typically, you create objects within the `main()` function because it serves as the entry point of your Kotlin program. +You can create objects wherever you need them, inside the `main()` function, within other functions, or as part of a class. -When the program runs, execution starts from the `main()` function, so this 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. +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. Let's look at an example of how object creation works. @@ -626,98 +654,15 @@ fun main() { You don't need to annotate abstract classes or functions with the `open` keyword because they are implicitly open and designed to be inherited. -Similarly, you can override a non-abstract `open` member with an abstract one: - -## 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.") - } -} -``` - -You don't need to annotate abstract classes or functions with the `open` keyword because they are implicitly -open and designed to be inherited. - -Similarly, you can override a non-abstract `open` member with an abstract one: - -```kotlin -// Abstract class with a primary constructor that defines 'name' -abstract class Person(val name: String) { - // Non-abstract open member that allows overriding - open fun greet() { - println("Hello, my name is $name.") - } - - // Abstract member - abstract fun introduce() -} - -// Subclass that overrides the 'greet()' function with an abstract one -abstract class Student(name: String, val school: String) : Person(name) { - // Overrides the non-abstract 'greet()' function with an abstract one - abstract override fun greet() - - // Overrides the abstract 'introduce()' function with an abstract one - override fun introduce() { - println("I am $name, and I study at $school.") - } -} - -``` +[For more details about the `open` keyword, see Inheritance](inheritance.md#open-keyword). ## Companion objects 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 having a class object but still needs access -to the internals of the class, you can define it inside `companion object` within that 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: ```kotlin // Class with a primary constructor that defines the 'name' property 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: From e4e8947dc35750ef934ba0822eb67479166c9a09 Mon Sep 17 00:00:00 2001 From: alepedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Mon, 18 Aug 2025 11:38:13 +0200 Subject: [PATCH 4/8] Review second round --- docs/topics/classes.md | 466 +++++++++++++------------------------ docs/topics/exceptions.md | 2 +- docs/topics/inheritance.md | 18 +- 3 files changed, 176 insertions(+), 310 deletions(-) diff --git a/docs/topics/classes.md b/docs/topics/classes.md index d0c0d86776f..a8f48a0c08a 100644 --- a/docs/topics/classes.md +++ b/docs/topics/classes.md @@ -4,7 +4,8 @@ Like other object-oriented languages, Kotlin uses _classes_ to encapsulate data 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 +create via [constructors](#constructors-and-initializer-blocks). +When you [create an instance of a class](#creating-instances-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 @@ -28,20 +29,28 @@ The class declaration consists of: * [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: +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 with a primary constructor that initializes the 'name' property +// Class with name and age properties declared in the primary constructor, but without a body +class Person(val name: String, var age: Int) +``` + +Here's an example of a class with a header, body, and an [instance created](#creating-instances-of-classes) from it: + +```kotlin +// Person class with a primary constructor that initializes the name property class Person(val name: String) { - // Class body with `age` property + // Class body with age property var age: Int = 0 } fun main() { - // Creates an object of the Person class by calling the constructor + // Creates an instance of the Person class by calling the constructor val person = Person("Alice") - // Access the object's properties + // Accesses the instance's properties println(person.name) // Alice println(person.age) @@ -50,46 +59,90 @@ fun main() { ``` {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`. +## Creating instances of classes + +An instance is created +when you use the class as a blueprint to build a real thing to work with in your program. + +To create an instance of a class, use the class name followed by parentheses (`()`), similar to calling a function: + +```kotlin +// Creates an instance of the Person class +val person1 = Person() +``` -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. +In Kotlin, you can create instances: -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). +* **Without arguments** (`()`): creates an instance using the default values, if they are declared in the class. +* **With arguments** (`(value)`): creates an instance by passing specific values. -Both the class header and class body can be minimal. If the class does not have a body, you can omit the curly braces `{}`: +You can assign the created instance to a mutable (`var`) or read-only (`val`) [variable](basic-syntax.md#variables): ```kotlin -// Class with name and primary constructor, but without body -class Person(val name: String, var age: Int) +// Creates an instance using the default value and assigns it to a mutable variable +var person1 = Person() + +// Creates an instance by passing a specific value and assigns it to a read-only variable +val person2 = Person("Joe") +``` + +It's possible to create instances wherever you need them, inside the [`main()` function](basic-syntax.md#program-entry-point), within other functions, or inside another class. +Additionally, you can create instances inside another function and call that function from `main()`. + +The following code declares a `Person` class with a property for storing a name. +It also demonstrates +how to create an instance 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 instance using the default constructor's value + val person1 = Person() + + // Creates an instance by passing a specific value + val person2 = Person("Joe") + + // Accesses the instances' name property + println(person1.name) + // Sebastian + println(person2.name) + // Joe +} ``` +{kotlin-runnable="true"} + +> In Kotlin, unlike other object-oriented programming languages such as Java, +> there is no need for the `new` keyword when creating class instances. +> +{style="note"} + +For information about creating instances of nested, inner, and anonymous inner classes, +see the [Nested classes](nested-classes.md) section. + +When creating class instances, you call one of its constructors and provide arguments for the constructor parameters. +For more details, see the next section about [Constructors and initializer blocks](#constructors-and-initializer-blocks). ## 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. +A class in Kotlin can have a [_primary constructor_](#primary-constructor) and one or more [_secondary constructors_](#secondary-constructors). -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. +The primary constructor is the main way to initialize a class; you declare it in the class header. +A secondary constructor provides additional initialization logic; you declare it in the class body. -### Primary constructor +Both primary and secondary constructors are optional, and at least one constructor should be declared in a class. -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). +### Primary constructor -Let's see examples of primary constructors and objects created based on them. +The primary constructor sets up the initial state of an instance when [it is created](#creating-instances-of-classes). -To declare a primary constructor, use the class header after the class name: +To declare a primary constructor, place it in the class header after the class name: ```kotlin class Person constructor(name: String) { /*...*/ } ``` -In this example, the primary constructor initializes the class `Person` by defining a parameter `name` of type `String`. - If the primary constructor does not have any annotations or [visibility modifiers](visibility-modifiers.md#constructors), you can omit the `constructor` keyword: @@ -97,113 +150,59 @@ you can omit the `constructor` keyword: 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: +The primary constructor can declare parameters as properties. Use the `val` keyword before the argument name to declare a read-only property +and the `var` keyword for a mutable property. ```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: +These constructor parameter properties are stored as part of the instance and are accessible from outside the class. +It's also possible to declare primary constructor parameters only, not stored in the instance, and available only within the 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." +// Primary constructor parameter that is also a property +class Person2(val name: String) { + fun greet() { + println("Hello, $name") } } -``` - -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) +// Primary constructor parameter only (not stored as a property) +class Person1(name: String) { + fun greet() { + println("Hello, $name") + } +} ``` -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: +Properties declared in the primary constructor are accessible to +[member functions](functions.md) of a class: ```kotlin -// Class with a primary constructor defining properties +// Class with a primary constructor declaring 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." + return "Hi, I'm $name and I'm $age years old." } } - -fun main() { - // 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"} - -[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 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: +You can also assign default values to properties in the primary constructor: ```kotlin class Person(val name: String = "John", var age: Int = 30) { /*...*/ } ``` -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: +If no value is passed during [instance creation](#creating-instances-of-classes), the property uses the default value: ```kotlin -// Class with a primary constructor including default values for 'name' and 'age' +// 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 + // Creates an instance using default values val person = Person() println("Name: ${person.name}, Age: ${person.age}") // Name: John, Age: 30 @@ -211,100 +210,75 @@ fun main() { ``` {kotlin-runnable="true"} -Also, you can use the primary constructor parameters to initialize additional class properties directly in the class body: +Use the primary constructor parameters to initialize additional class properties directly in the class body: ```kotlin -// 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" -} -``` - -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 with a primary constructor including default values for name and age class Person( val name: String = "John", var age: Int = 30 ) { - // Property 'description' initialized using the primary constructor + // description property initialized in the class body using the primary constructor parameters val description: String = "Name: $name, Age: $age" } fun main() { - // Create an instance of the 'Person' class + // Creates an instance of the Person class val person = Person() - // Accesses the 'description' property from the 'person' object + // Accesses the description property println(person.description) // Name: John, Age: 30 } ``` {kotlin-runnable="true"} -### Initializer blocks - -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. - -If you want to run some code during [object creation](#creating-objects-of-classes), use _initializer blocks_ inside the class body. - -Declare initializer blocks with the `init` keyword followed by curly braces `{}`. Write any code that you want to run -within the curly braces: +Additionally, you can use a [trailing comma](coding-conventions.md#trailing-commas) in constructor declarations: ```kotlin -// 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") - } -} +class Person( + val name: String, + val lastName: String, + var age: Int, +) { /*...*/ } ``` -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. +### Initializer blocks + +The primary constructor can't contain runnable code. Its purpose is to initialize the class and set its +properties. If you want to run some code during [instance creation](#creating-instances-of-classes), use _initializer blocks_ inside the class body. -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: +Declare initializer blocks with the `init` keyword followed by curly braces `{}`. +Write within the curly braces any code that you want to run during initialization: ```kotlin //sampleStart -// Class with a primary constructor that initializes 'name' and 'age' +// 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 + // Initializer block executed when an instance is created println("Person created: $name, age $age.") // Person created: John, age 30. } } fun main() { - // Creates an object of the 'Person' class + // Creates an instance 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. - -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`: +Add as many initializer blocks (`init {}`) as you need. They run in the order in which they appear in the class body, +interleaved with property initializers: ```kotlin //sampleStart -// Class with a primary constructor that initializes 'name' and 'age' +// 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 + // Runs first when an instance is created println("Person created: $name, age $age.") // Person created: John, age 30. } @@ -322,7 +296,7 @@ class Person(val name: String, var age: Int) { } fun main() { - // Creates an object of the 'Person' class + // Creates an instance of the Person class Person("John", 30) } //sampleEnd @@ -335,32 +309,19 @@ the `name` and `age` parameters from the primary constructor. ### Secondary constructors 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 +Secondary constructors allow you to provide different ways to initialize an instance 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: +keyword inside the class body with the constructor parameters within parentheses. Use curly braces with the constructor logic inside: ```kotlin -// Class header with primary constructor that initializes 'name' and 'age' +// Class header with a 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: - -```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 + // Secondary constructor that takes a String age and converts it to an integer constructor(name: String, age: String) : this(name, age.toIntOrNull() ?: 0) { println("$name created with converted age: $age") // Bob created with converted age: 8 @@ -374,8 +335,12 @@ fun main() { ``` {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`. +> The expression `age.toIntOrNull() ?: 0` converts the `age` string to an integer. For more information about this expression, see [Null safety](null-safety.md). +> +{style="tip"} + +In the code above, the secondary constructor delegates to the primary constructor via the `this` keyword, +passing `name` and the default `age` value. 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. @@ -387,25 +352,7 @@ Constructor delegation can be: Here's an example demonstrating how direct and indirect delegation works: ```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 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") - } -} -``` - -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 header with a primary constructor that initializes name and age class Person( val name: String, var age: Int @@ -417,7 +364,7 @@ class Person( // Person created with default age: 0 and name: Bob. } - // Secondary constructor with indirect delegation: 'this("Bob")' -> 'constructor(name: String)' -> primary constructor + // 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. @@ -425,28 +372,24 @@ class Person( } fun main() { - // Creates an object based on the direct delegation + // Creates an instance based on the direct delegation Person("Alice") - // Creates an object based on the indirect delegation + // Creates an instance 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: +Given that secondary constructors delegate to the primary constructor first, all initializer blocks +and property initializers run before the body of the secondary constructor. Even if the class has no primary constructor, +the delegation still happens implicitly: ```kotlin -//sampleStart // Class header with no primary constructor class Person { - // Initializer block executed when an object is created + // Initializer block executed when an instance is created init { // Runs before the secondary constructor println("1. First initializer block runs") @@ -462,17 +405,14 @@ class Person { } fun main() { - // Creates an object of the 'Person' class using the secondary constructor + // Creates an instance of the Person class Person(1) } -//sampleEnd ``` {kotlin-runnable="true"} -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: +If there's a class that does not declare any constructors (primary or secondary), it will have an implicit primary constructor +with no parameters: ```kotlin // Class with no explicit constructors @@ -481,12 +421,12 @@ class Person { } fun main() { - // Creates an object of the 'Person' class using the implicit primary constructor + // Creates an instance 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. +The visibility of this implicit primary constructor is 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 @@ -497,7 +437,7 @@ class Person private constructor() { /*...*/ } > 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 +> or [Spring Data JPA](https://spring.io/projects/spring-data-jpa), which create class instances through > parameterless constructors. > > In the following example, Kotlin implicitly provides a parameterless constructor `Person()` that uses the default value `""`: @@ -508,74 +448,6 @@ class Person private constructor() { /*...*/ } > {style="note"} -## 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. - -As you can see, you can assign the created object to a mutable (`var`) or immutable (`val`) [variable](basic-syntax.md#variables): - -```kotlin -// Creates an object by passing a specific value and assigns it to a mutable variable -var person2 = Person("Joe") - -// Creates an object using the default constructor's value and assigns it to an immutable variable -val person1 = Person() -``` - -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. - -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"} - -> 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"} - -Additionally, you can create objects inside another function and call that function from `main()`. - -For information about creating objects of nested, inner, and anonymous inner classes, -see the [Nested classes](nested-classes.md) section. - ## Inheritance Class inheritance in Kotlin allows you to create a new class (derived class) from an existing class (base class), @@ -590,38 +462,34 @@ inheriting its properties and functions while adding or modifying behavior. 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 +An abstract class can declare abstract properties and functions, which must be implemented by subclasses. -You can declare an abstract class using the `abstract` modifier: - -```kotlin -abstract class Person -``` - -Like any other class, abstract classes can also have constructors. -These constructors initialize class properties and enforce required parameters for subclasses: +Abstract classes can also have constructors. +These constructors initialize class properties and enforce required parameters for subclasses. +Declare an abstract class using the `abstract` modifier: ```kotlin abstract class Person(val name: String, val age: Int) ``` 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. +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: +Abstract members do not have an implementation +in the abstract class. To provide one, you declare an `override` function +in the subclass or inheriting class: ```kotlin -// Abstract class with a primary constructor that defines 'name' and 'age' +// Abstract class with a primary constructor that declares 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 member (doesn't provide an implementation, must be implemented by subclasses) abstract fun introduce() - // Non-abstract member (has a default implementation) + // Non-abstract member (has an implementation) fun greet() { println("Hello, my name is $name.") // Hello, my name is Alice. @@ -641,7 +509,7 @@ class Student( } fun main() { - // Creates an object of the Student class + // Creates an instance of the Student class val student = Student("Alice", 20, "Engineering University") // Calls the non-abstract member student.greet() @@ -661,11 +529,11 @@ open and designed to be inherited. 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: +Suppose you need to write a function that can be called without creating an instance of a class, but it is still logically +connected to the class (such as a factory function). In that case, you can declare it inside a companion [object declaration](object-declarations.md) within the class: ```kotlin -// Class with a primary constructor that defines the 'name' property +// Class with a primary constructor that declares the name property class Person( val name: String ) { @@ -676,7 +544,7 @@ class Person( } fun main() { - // Calls the function without creating an object of the class + // Calls the function without creating an instance of the class val anonymous = Person.createAnonymous() println(anonymous.name) // Anonymous diff --git a/docs/topics/exceptions.md b/docs/topics/exceptions.md index 8be5c3343c0..42749c24abd 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-objects-of-classes), and throwing one creates an instance of an exception class. +Exceptions are [objects](classes.md#creating-instances-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 4c7604735a6..1e40f96aada 100644 --- a/docs/topics/inheritance.md +++ b/docs/topics/inheritance.md @@ -43,10 +43,11 @@ 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`: +By default, Kotlin classes and their members are _final_, meaning they cannot be inherited from (for classes) or overridden +(for members) unless you explicitly mark them as `open`: ```kotlin -// Base class with the 'open' keyword to allow inheritance +// Base class with the open keyword to allow inheritance open class Person( val name: String ) { @@ -56,7 +57,7 @@ open class Person( } } -// Subclass inheriting from 'Person' and overriding the 'introduce()' function +// Subclass inheriting from Person and overriding the introduce() function class Student( name: String, val school: String @@ -67,16 +68,13 @@ class Student( } ``` -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 +is also 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 +// 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() { @@ -84,9 +82,9 @@ open class Person(val name: String) { } } -// Subclass inheriting from 'Person' and overriding the 'introduce()' function +// 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 + // The final keyword prevents further overriding in subclasses final override fun introduce() { println("Hi, I'm $name, and I study at $school.") } From 549798c35a7be73f39189b1f1326b2917cecb7a3 Mon Sep 17 00:00:00 2001 From: alepedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Wed, 20 Aug 2025 12:16:04 +0200 Subject: [PATCH 5/8] SME review --- docs/topics/classes.md | 77 ++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/docs/topics/classes.md b/docs/topics/classes.md index a8f48a0c08a..b2f256176e2 100644 --- a/docs/topics/classes.md +++ b/docs/topics/classes.md @@ -33,21 +33,22 @@ 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 with name and age properties declared in the primary constructor, but without a body +// Class with primary constructor, but without a body class Person(val name: String, var age: Int) ``` Here's an example of a class with a header, body, and an [instance created](#creating-instances-of-classes) from it: ```kotlin -// Person class with a primary constructor that initializes the name property +// Person class with a primary constructor +// that nitializes the name property class Person(val name: String) { // Class body with age property var age: Int = 0 } fun main() { - // Creates an instance of the Person class by calling the constructor + // Creates an instance of Person class by calling the constructor val person = Person("Alice") // Accesses the instance's properties @@ -62,9 +63,9 @@ fun main() { ## Creating instances of classes An instance is created -when you use the class as a blueprint to build a real thing to work with in your program. +when you use the class as a blueprint to build a real object to work with in your program. -To create an instance of a class, use the class name followed by parentheses (`()`), similar to calling a function: +To create an instance of a class, use the class name followed by parentheses (`()`), similar to calling a [function](functions.md): ```kotlin // Creates an instance of the Person class @@ -79,10 +80,12 @@ In Kotlin, you can create instances: You can assign the created instance to a mutable (`var`) or read-only (`val`) [variable](basic-syntax.md#variables): ```kotlin -// Creates an instance using the default value and assigns it to a mutable variable +// Creates an instance using the default value +// and assigns it to a mutable variable var person1 = Person() -// Creates an instance by passing a specific value and assigns it to a read-only variable +// Creates an instance by passing a specific value +// and assigns it to a read-only variable val person2 = Person("Joe") ``` @@ -94,7 +97,8 @@ It also demonstrates how to create an instance 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 header with a primary constructor +// that initializes name with a default value class Person(val name: String = "Sebastian") fun main() { @@ -151,14 +155,14 @@ class Person(name: String) { /*...*/ } ``` The primary constructor can declare parameters as properties. Use the `val` keyword before the argument name to declare a read-only property -and the `var` keyword for a mutable property. +and the `var` keyword for a mutable property: ```kotlin class Person(val name: String, var age: Int) { /*...*/ } ``` These constructor parameter properties are stored as part of the instance and are accessible from outside the class. -It's also possible to declare primary constructor parameters only, not stored in the instance, and available only within the class: +It's also possible to declare primary constructor parameters only, not stored in the instance, and available only within the class body: ```kotlin // Primary constructor parameter that is also a property @@ -170,8 +174,11 @@ class Person2(val name: String) { // Primary constructor parameter only (not stored as a property) class Person1(name: String) { + // Must be assigned to a property to be usable later + val n: String = name + fun greet() { - println("Hello, $name") + println("Hello, $n") } } ``` @@ -198,7 +205,8 @@ class Person(val name: String = "John", var age: Int = 30) { /*...*/ } If no value is passed during [instance creation](#creating-instances-of-classes), the property uses the default value: ```kotlin -// Class with a primary constructor including default values for name and age +// Class with a primary constructor +// including default values for name and age class Person(val name: String = "John", var age: Int = 30) fun main() { @@ -213,12 +221,14 @@ fun main() { Use the primary constructor parameters to initialize additional class properties directly in the class body: ```kotlin -// Class with a primary constructor including default values for name and age +// Class with a primary constructor +// including default values for name and age class Person( val name: String = "John", var age: Int = 30 ) { - // description property initialized in the class body using the primary constructor parameters + // description property initialized in the class body + // using the primary constructor parameters val description: String = "Name: $name, Age: $age" } @@ -232,7 +242,7 @@ fun main() { ``` {kotlin-runnable="true"} -Additionally, you can use a [trailing comma](coding-conventions.md#trailing-commas) in constructor declarations: +As with functions, you can use [trailing comma](coding-conventions.md#trailing-commas) in constructor declarations: ```kotlin class Person( @@ -244,14 +254,16 @@ class Person( ### Initializer blocks -The primary constructor can't contain runnable code. Its purpose is to initialize the class and set its -properties. If you want to run some code during [instance creation](#creating-instances-of-classes), use _initializer blocks_ inside the class body. +The purpose of the primary constructor is to initialize the class and set its +properties. In most cases, this initialization doesn’t require complex code. + +If you need to run complex code blocks when the primary constructor is executed during [instance creation](#creating-instances-of-classes), +use _initializer blocks_ inside the class body. Declare initializer blocks with the `init` keyword followed by curly braces `{}`. Write within the curly braces any code that you want to run during initialization: ```kotlin -//sampleStart // Class with a primary constructor that initializes name and age class Person(val name: String, var age: Int) { init { @@ -265,7 +277,6 @@ fun main() { // Creates an instance of the Person class Person("John", 30) } -//sampleEnd ``` {kotlin-runnable="true"} @@ -306,6 +317,16 @@ fun main() { 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. +A common use of `init` blocks is data validation. For example, by calling the [`require` function](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/require.html): + +```kotlin +class Person(val age: Int) { + init { + require(age > 0, "age must be positive") + } +} +``` + ### Secondary constructors In Kotlin, a secondary constructor is an additional constructor that a class can have beyond its primary constructor. @@ -321,7 +342,9 @@ keyword inside the class body with the constructor parameters within parentheses ```kotlin // Class header with a 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 to an integer + + // Secondary constructor that takes a String age + // and converts it to an integer constructor(name: String, age: String) : this(name, age.toIntOrNull() ?: 0) { println("$name created with converted age: $age") // Bob created with converted age: 8 @@ -340,7 +363,7 @@ fun main() { {style="tip"} In the code above, the secondary constructor delegates to the primary constructor via the `this` keyword, -passing `name` and the default `age` value. +passing `name` and the `age` value converted to an integer. 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. @@ -357,14 +380,16 @@ class Person( val name: String, var age: Int ) { - // Secondary constructor with direct delegation to the primary constructor + // 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 + // 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. @@ -421,7 +446,8 @@ class Person { } fun main() { - // Creates an instance of the Person class using the implicit primary constructor + // Creates an instance of the Person class + // using the implicit primary constructor val person = Person() } ``` @@ -486,7 +512,8 @@ abstract class Person( val name: String, val age: Int ) { - // Abstract member (doesn't provide an implementation, must be implemented by subclasses) + // Abstract member + // Doesn't provide implementation, must be implemented by subclasses abstract fun introduce() // Non-abstract member (has an implementation) From e72d2fee7da0f80acc816db7d74d7f50f949f5d6 Mon Sep 17 00:00:00 2001 From: alepedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Fri, 19 Sep 2025 16:30:32 +0200 Subject: [PATCH 6/8] Sarah review --- docs/topics/classes.md | 180 +++++++++++++++++++------------------ docs/topics/inheritance.md | 4 +- 2 files changed, 95 insertions(+), 89 deletions(-) diff --git a/docs/topics/classes.md b/docs/topics/classes.md index b2f256176e2..178f201bda4 100644 --- a/docs/topics/classes.md +++ b/docs/topics/classes.md @@ -8,7 +8,7 @@ create via [constructors](#constructors-and-initializer-blocks). When you [create an instance of a class](#creating-instances-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 +Kotlin offers concise syntax for declaring classes. To declare a class, use the `class` keyword followed by the class name: ```kotlin @@ -30,25 +30,26 @@ The class declaration consists of: * [Object declarations](object-declarations.md) Both the class header and class body can be minimal. -If the class does not have a body, you can omit the curly braces `{}`: +If the class doesn't have a body, you can omit the curly braces `{}`: ```kotlin // Class with primary constructor, but without a body class Person(val name: String, var age: Int) ``` -Here's an example of a class with a header, body, and an [instance created](#creating-instances-of-classes) from it: +Here's an example that declares a class with a header and body, +then [creates an instance](#creating-instances-of-classes) from it: ```kotlin // Person class with a primary constructor -// that nitializes the name property +// that initializes the name property class Person(val name: String) { // Class body with age property var age: Int = 0 } fun main() { - // Creates an instance of Person class by calling the constructor + // Creates an instance of the Person class by calling the constructor val person = Person("Alice") // Accesses the instance's properties @@ -58,35 +59,35 @@ fun main() { // 0 } ``` -{kotlin-runnable="true"} +{kotlin-runnable="true" id="class-with-header-and-body"} ## Creating instances of classes An instance is created when you use the class as a blueprint to build a real object to work with in your program. -To create an instance of a class, use the class name followed by parentheses (`()`), similar to calling a [function](functions.md): +To create an instance of a class, use the class name followed by parentheses `()`, similar to calling a [function](functions.md): ```kotlin // Creates an instance of the Person class -val person1 = Person() +val anonymousUser = Person() ``` In Kotlin, you can create instances: -* **Without arguments** (`()`): creates an instance using the default values, if they are declared in the class. -* **With arguments** (`(value)`): creates an instance by passing specific values. +* **Without arguments** (`Person()`): creates an instance using the default values, if they are declared in the class. +* **With arguments** (`Person(value)`): creates an instance by passing specific values. You can assign the created instance to a mutable (`var`) or read-only (`val`) [variable](basic-syntax.md#variables): ```kotlin // Creates an instance using the default value // and assigns it to a mutable variable -var person1 = Person() +var anonymousUser = Person() // Creates an instance by passing a specific value // and assigns it to a read-only variable -val person2 = Person("Joe") +val namedUser = Person("Joe") ``` It's possible to create instances wherever you need them, inside the [`main()` function](basic-syntax.md#program-entry-point), within other functions, or inside another class. @@ -103,21 +104,21 @@ class Person(val name: String = "Sebastian") fun main() { // Creates an instance using the default constructor's value - val person1 = Person() + val anonymousUser = Person() // Creates an instance by passing a specific value - val person2 = Person("Joe") + val namedUser = Person("Joe") // Accesses the instances' name property - println(person1.name) + println(anonymousUser.name) // Sebastian - println(person2.name) + println(namedUser.name) // Joe } ``` -{kotlin-runnable="true"} +{kotlin-runnable="true" id="create-instance-of-a-class"} -> In Kotlin, unlike other object-oriented programming languages such as Java, +> In Kotlin, unlike other object-oriented programming languages, > there is no need for the `new` keyword when creating class instances. > {style="note"} @@ -125,21 +126,21 @@ fun main() { For information about creating instances of nested, inner, and anonymous inner classes, see the [Nested classes](nested-classes.md) section. -When creating class instances, you call one of its constructors and provide arguments for the constructor parameters. -For more details, see the next section about [Constructors and initializer blocks](#constructors-and-initializer-blocks). - ## Constructors and initializer blocks -A class in Kotlin can have a [_primary constructor_](#primary-constructor) and one or more [_secondary constructors_](#secondary-constructors). +When you create a class instance, you call one of its constructors. A class in Kotlin can have a +[_primary constructor_](#primary-constructor) and one or more [_secondary constructors_](#secondary-constructors). -The primary constructor is the main way to initialize a class; you declare it in the class header. -A secondary constructor provides additional initialization logic; you declare it in the class body. +The primary constructor is the main way to initialize a class. +You declare it in the class header. +A secondary constructor provides additional initialization logic. +You declare it in the class body. -Both primary and secondary constructors are optional, and at least one constructor should be declared in a class. +Both primary and secondary constructors are optional, but a class must have at least one constructor. ### Primary constructor -The primary constructor sets up the initial state of an instance when [it is created](#creating-instances-of-classes). +The primary constructor sets up the initial state of an instance when [it's created](#creating-instances-of-classes). To declare a primary constructor, place it in the class header after the class name: @@ -147,7 +148,7 @@ To declare a primary constructor, place it in the class header after the class n class Person constructor(name: String) { /*...*/ } ``` -If the primary constructor does not have any annotations or [visibility modifiers](visibility-modifiers.md#constructors), +If the primary constructor doesn't have any annotations or [visibility modifiers](visibility-modifiers.md#constructors), you can omit the `constructor` keyword: ```kotlin @@ -161,30 +162,33 @@ and the `var` keyword for a mutable property: class Person(val name: String, var age: Int) { /*...*/ } ``` -These constructor parameter properties are stored as part of the instance and are accessible from outside the class. -It's also possible to declare primary constructor parameters only, not stored in the instance, and available only within the class body: +These constructor parameter properties are stored as part of the instance and are accessible from outside the class. + +It's also possible to declare primary constructor parameters that are not properties. +These parameters don't have +`val` or `var` in front of them, so they are not stored in the instance and are available only within the class body: ```kotlin // Primary constructor parameter that is also a property -class Person2(val name: String) { +class PersonWithProperty(val name: String) { fun greet() { println("Hello, $name") } } // Primary constructor parameter only (not stored as a property) -class Person1(name: String) { +class PersonWithAssignment(name: String) { // Must be assigned to a property to be usable later - val n: String = name + val displayName: String = name fun greet() { - println("Hello, $n") + println("Hello, $displayName") } } ``` -Properties declared in the primary constructor are accessible to -[member functions](functions.md) of a class: +Properties declared in the primary constructor are accessible by +[member functions](functions.md) of the class: ```kotlin // Class with a primary constructor declaring properties @@ -202,7 +206,8 @@ You can also assign default values to properties in the primary constructor: class Person(val name: String = "John", var age: Int = 30) { /*...*/ } ``` -If no value is passed during [instance creation](#creating-instances-of-classes), the property uses the default value: +If no value is passed to the constructor during [instance creation](#creating-instances-of-classes), +properties use their default value: ```kotlin // Class with a primary constructor @@ -216,9 +221,9 @@ fun main() { // Name: John, Age: 30 } ``` -{kotlin-runnable="true"} +{kotlin-runnable="true" id="class-with-primary-constructor"} -Use the primary constructor parameters to initialize additional class properties directly in the class body: +You can use the primary constructor parameters to initialize additional class properties directly in the class body: ```kotlin // Class with a primary constructor @@ -227,8 +232,8 @@ class Person( val name: String = "John", var age: Int = 30 ) { - // description property initialized in the class body - // using the primary constructor parameters + // Initializes the description property + // from the primary constructor parameters val description: String = "Name: $name, Age: $age" } @@ -240,9 +245,9 @@ fun main() { // Name: John, Age: 30 } ``` -{kotlin-runnable="true"} +{kotlin-runnable="true" id="class-with-default-values"} -As with functions, you can use [trailing comma](coding-conventions.md#trailing-commas) in constructor declarations: +As with functions, you can use [trailing commas](coding-conventions.md#trailing-commas) in constructor declarations: ```kotlin class Person( @@ -254,11 +259,11 @@ class Person( ### Initializer blocks -The purpose of the primary constructor is to initialize the class and set its -properties. In most cases, this initialization doesn’t require complex code. +The primary constructor initializes the class and sets its properties. +In most cases, you can handle this with simple code. -If you need to run complex code blocks when the primary constructor is executed during [instance creation](#creating-instances-of-classes), -use _initializer blocks_ inside the class body. +If you need to perform more complex operations during [instance creation](#creating-instances-of-classes), +place that logic in _initializer blocks_ inside the class body. These blocks run when the primary constructor executes. Declare initializer blocks with the `init` keyword followed by curly braces `{}`. Write within the curly braces any code that you want to run during initialization: @@ -267,7 +272,7 @@ Write within the curly braces any code that you want to run during initializatio // Class with a primary constructor that initializes name and age class Person(val name: String, var age: Int) { init { - // Initializer block executed when an instance is created + // Initializer block runs when an instance is created println("Person created: $name, age $age.") // Person created: John, age 30. } @@ -278,7 +283,7 @@ fun main() { Person("John", 30) } ``` -{kotlin-runnable="true"} +{kotlin-runnable="true" id="class-with-initializer-block"} Add as many initializer blocks (`init {}`) as you need. They run in the order in which they appear in the class body, interleaved with property initializers: @@ -312,12 +317,12 @@ fun main() { } //sampleEnd ``` -{kotlin-runnable="true"} +{kotlin-runnable="true" id="class-with-second-initializer-block"} -You can use the primary constructor parameters in the initializer blocks. For example, in the code above, the first and second initializers use +You can use primary constructor parameters in initializer blocks. For example, in the code above, the first and second initializers use the `name` and `age` parameters from the primary constructor. -A common use of `init` blocks is data validation. For example, by calling the [`require` function](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/require.html): +A common use case for `init` blocks is data validation. For example, by calling the [`require` function](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/require.html): ```kotlin class Person(val age: Int) { @@ -329,22 +334,20 @@ class Person(val age: Int) { ### Secondary constructors -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 instance 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. +In Kotlin, secondary constructors are additional constructors that a class can have beyond its primary constructor. +Secondary constructors are useful +when you need multiple ways to initialize a class or for [Java interoperability](java-to-kotlin-interop.md). To declare a secondary constructor, use the `constructor` -keyword inside the class body with the constructor parameters within parentheses. Use curly braces with the constructor logic inside: +keyword inside the class body with the constructor parameters within parentheses `()`. +Add the constructor logic within curly braces `{}`: ```kotlin // Class header with a 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 to an integer + // Secondary constructor that takes age as a + // String and converts it to an Int constructor(name: String, age: String) : this(name, age.toIntOrNull() ?: 0) { println("$name created with converted age: $age") // Bob created with converted age: 8 @@ -352,13 +355,13 @@ class Person(val name: String, var age: Int) { } fun main() { - // Uses the secondary constructor with a String age + // Uses the secondary constructor with age as a String Person("Bob", "8") } ``` -{kotlin-runnable="true"} +{kotlin-runnable="true" id="class-with-secondary-constructor"} -> The expression `age.toIntOrNull() ?: 0` converts the `age` string to an integer. For more information about this expression, see [Null safety](null-safety.md). +> The expression `age.toIntOrNull() ?: 0` uses the Elvis operator. For more information, see [Null safety](null-safety.md#elvis-operator). > {style="tip"} @@ -404,7 +407,7 @@ fun main() { Person() } ``` -{kotlin-runnable="true"} +{kotlin-runnable="true" id="class-delegation"} In classes with initializer blocks (`init {}`), the code within these blocks becomes part of the primary constructor. Given that secondary constructors delegate to the primary constructor first, all initializer blocks @@ -414,7 +417,7 @@ the delegation still happens implicitly: ```kotlin // Class header with no primary constructor class Person { - // Initializer block executed when an instance is created + // Initializer block runs when an instance is created init { // Runs before the secondary constructor println("1. First initializer block runs") @@ -434,9 +437,11 @@ fun main() { Person(1) } ``` -{kotlin-runnable="true"} +{kotlin-runnable="true" id="class-delegation-sequence"} + +### Classes without constructors -If there's a class that does not declare any constructors (primary or secondary), it will have an implicit primary constructor +Classes that don't declare any constructors (primary or secondary) have an implicit primary constructor with no parameters: ```kotlin @@ -479,21 +484,21 @@ class Person private constructor() { /*...*/ } 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. +> For detailed information about inheritance hierarchies and how to use of the `open` keyword, see the [Inheritance](inheritance.md) section. > {style="note"} ## Abstract classes -An abstract class in Kotlin is a class that cannot be instantiated directly and is designed to be inherited -by other classes. +In Kotlin, abstract classes are classes that can't be instantiated directly. They are designed to be inherited by other +classes which define their actual behavior. This behavior is called an _implementation_. An abstract class can declare abstract properties and functions, which must be implemented by subclasses. Abstract classes can also have constructors. These constructors initialize class properties and enforce required parameters for subclasses. -Declare an abstract class using the `abstract` modifier: +Declare an abstract class using the `abstract` keyword: ```kotlin abstract class Person(val name: String, val age: Int) @@ -502,9 +507,13 @@ abstract class Person(val name: String, val age: Int) 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. To provide one, you declare an `override` function -in the subclass or inheriting class: +You don't need to annotate abstract classes or functions with the `open` +keyword because they are implicitly inheritable by default. +For more details about the `open` keyword, see [Inheritance](inheritance.md#open-keyword). + +Abstract members don't have an implementation +in the abstract class. +You define the implementation in a subclass or inheriting class with an `override` function or property: ```kotlin // Abstract class with a primary constructor that declares name and age @@ -513,7 +522,8 @@ abstract class Person( val age: Int ) { // Abstract member - // Doesn't provide implementation, must be implemented by subclasses + // Doesn't provide implementation, + // and it must be implemented by subclasses abstract fun introduce() // Non-abstract member (has an implementation) @@ -544,17 +554,13 @@ fun main() { 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). +{kotlin-runnable="true" id="abstract-class"} ## Companion objects -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. +In Kotlin, each class can have a [companion object](object-declarations.md#companion-objects). +Companion objects are a type of object declaration +that allows you to access its members using the class name without creating a class instance. Suppose you need to write a function that can be called without creating an instance of a class, but it is still logically connected to the class (such as a factory function). In that case, you can declare it inside a companion [object declaration](object-declarations.md) within the class: @@ -577,11 +583,11 @@ fun main() { // Anonymous } ``` -{kotlin-runnable="true"} +{kotlin-runnable="true" id="class-with-companion-object"} -If you declare `companion object` inside your class, +If you declare a 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). +> For more information, see [Companion objects](object-declarations.md#companion-objects). > -{style="note"} \ No newline at end of file +{style="tip"} \ No newline at end of file diff --git a/docs/topics/inheritance.md b/docs/topics/inheritance.md index 1e40f96aada..97dd707b2e4 100644 --- a/docs/topics/inheritance.md +++ b/docs/topics/inheritance.md @@ -82,9 +82,9 @@ open class Person(val name: String) { } } -// Subclass inheriting from Person and overriding the introduce() function +// A subclass that inherits from Person and overrides the introduce() function class Student(name: String, val school: String) : Person(name) { - // The final keyword prevents further overriding in subclasses + // The final keyword prevents further overrides in subclasses final override fun introduce() { println("Hi, I'm $name, and I study at $school.") } From 95723066b8e4c66c420fa97784ef2171ce97540e Mon Sep 17 00:00:00 2001 From: alepedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Mon, 29 Sep 2025 11:22:37 +0200 Subject: [PATCH 7/8] Switch output code comments --- docs/topics/classes.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/docs/topics/classes.md b/docs/topics/classes.md index 178f201bda4..1498c9b05f6 100644 --- a/docs/topics/classes.md +++ b/docs/topics/classes.md @@ -274,13 +274,13 @@ class Person(val name: String, var age: Int) { init { // Initializer block runs when an instance is created println("Person created: $name, age $age.") - // Person created: John, age 30. } } fun main() { // Creates an instance of the Person class Person("John", 30) + // Person created: John, age 30. } ``` {kotlin-runnable="true" id="class-with-initializer-block"} @@ -296,7 +296,6 @@ class Person(val name: String, var age: Int) { init { // Runs first when an instance is created println("Person created: $name, age $age.") - // Person created: John, age 30. } // Second initializer block @@ -306,7 +305,6 @@ class Person(val name: String, var age: Int) { println("$name is a minor.") } else { println("$name is an adult.") - // John is an adult. } } } @@ -314,6 +312,8 @@ class Person(val name: String, var age: Int) { fun main() { // Creates an instance of the Person class Person("John", 30) + // Person created: John, age 30. + // John is an adult. } //sampleEnd ``` @@ -350,13 +350,13 @@ class Person(val name: String, var age: Int) { // String and converts it to an Int 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 age as a String Person("Bob", "8") + // Bob created with converted age: 8 } ``` {kotlin-runnable="true" id="class-with-secondary-constructor"} @@ -387,24 +387,24 @@ class Person( // 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 instance based on the direct delegation Person("Alice") + // Person created with default age: 0 and name: Alice. // Creates an instance based on the indirect delegation Person() + // Person created with default age: 0 and name: Bob. + // New person created with default age: 0 and name: Bob. } ``` {kotlin-runnable="true" id="class-delegation"} @@ -421,20 +421,20 @@ class Person { init { // 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) { // Runs after the initializer block println("2. Person $i is created") - // 2. Person 1 created } } fun main() { // Creates an instance of the Person class Person(1) + // 1. First initializer block runs + // 2. Person 1 created } ``` {kotlin-runnable="true" id="class-delegation-sequence"} @@ -529,7 +529,6 @@ abstract class Person( // Non-abstract member (has an implementation) fun greet() { println("Hello, my name is $name.") - // Hello, my name is Alice. } } @@ -541,17 +540,20 @@ class Student( ) : 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. } } fun main() { // Creates an instance of the Student class val student = Student("Alice", 20, "Engineering University") + // Calls the non-abstract member student.greet() + // Hello, my name is Alice. + // Calls the overridden abstract member student.introduce() + // I am Alice, 20 years old, and I study at Engineering University. } ``` {kotlin-runnable="true" id="abstract-class"} From e4beeba553a627b98988f97b2decb9712267d587 Mon Sep 17 00:00:00 2001 From: alepedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Thu, 2 Oct 2025 17:07:11 +0200 Subject: [PATCH 8/8] Sarah second review --- docs/topics/classes.md | 28 ++++++++++++---------------- docs/topics/exceptions.md | 2 +- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/docs/topics/classes.md b/docs/topics/classes.md index 1498c9b05f6..b43ffffdfdf 100644 --- a/docs/topics/classes.md +++ b/docs/topics/classes.md @@ -5,7 +5,7 @@ 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-instances-of-classes), you are creating +When you [create an instance of a class](#creating-instances), you are creating a concrete object based on that blueprint. Kotlin offers concise syntax for declaring classes. To declare a class, use the `class` keyword @@ -29,7 +29,7 @@ The class declaration consists of: * [Nested and inner classes](nested-classes.md) * [Object declarations](object-declarations.md) -Both the class header and class body can be minimal. +You can keep both the class header and body to a bare minimum. If the class doesn't have a body, you can omit the curly braces `{}`: ```kotlin @@ -38,7 +38,7 @@ class Person(val name: String, var age: Int) ``` Here's an example that declares a class with a header and body, -then [creates an instance](#creating-instances-of-classes) from it: +then [creates an instance](#creating-instances) from it: ```kotlin // Person class with a primary constructor @@ -61,7 +61,7 @@ fun main() { ``` {kotlin-runnable="true" id="class-with-header-and-body"} -## Creating instances of classes +## Creating instances An instance is created when you use the class as a blueprint to build a real object to work with in your program. @@ -140,7 +140,7 @@ Both primary and secondary constructors are optional, but a class must have at l ### Primary constructor -The primary constructor sets up the initial state of an instance when [it's created](#creating-instances-of-classes). +The primary constructor sets up the initial state of an instance when [it's created](#creating-instances). To declare a primary constructor, place it in the class header after the class name: @@ -148,7 +148,7 @@ To declare a primary constructor, place it in the class header after the class n class Person constructor(name: String) { /*...*/ } ``` -If the primary constructor doesn't have any annotations or [visibility modifiers](visibility-modifiers.md#constructors), +If the primary constructor doesn't have any [annotations](annotations.md) or [visibility modifiers](visibility-modifiers.md#constructors), you can omit the `constructor` keyword: ```kotlin @@ -206,7 +206,7 @@ You can also assign default values to properties in the primary constructor: class Person(val name: String = "John", var age: Int = 30) { /*...*/ } ``` -If no value is passed to the constructor during [instance creation](#creating-instances-of-classes), +If no value is passed to the constructor during [instance creation](#creating-instances), properties use their default value: ```kotlin @@ -262,7 +262,7 @@ class Person( The primary constructor initializes the class and sets its properties. In most cases, you can handle this with simple code. -If you need to perform more complex operations during [instance creation](#creating-instances-of-classes), +If you need to perform more complex operations during [instance creation](#creating-instances), place that logic in _initializer blocks_ inside the class body. These blocks run when the primary constructor executes. Declare initializer blocks with the `init` keyword followed by curly braces `{}`. @@ -286,7 +286,7 @@ fun main() { {kotlin-runnable="true" id="class-with-initializer-block"} Add as many initializer blocks (`init {}`) as you need. They run in the order in which they appear in the class body, -interleaved with property initializers: +along with property initializers: ```kotlin //sampleStart @@ -484,9 +484,7 @@ class Person private constructor() { /*...*/ } 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 how to use of the `open` keyword, see the [Inheritance](inheritance.md) section. -> -{style="note"} +For detailed information about inheritance hierarchies and how to use of the `open` keyword, see the [Inheritance](inheritance.md) section. ## Abstract classes @@ -512,7 +510,7 @@ keyword because they are implicitly inheritable by default. For more details about the `open` keyword, see [Inheritance](inheritance.md#open-keyword). Abstract members don't have an implementation -in the abstract class. +in the abstract class. You define the implementation in a subclass or inheriting class with an `override` function or property: ```kotlin @@ -590,6 +588,4 @@ fun main() { If you declare a companion object inside your class, you can access its members using only the class name as a qualifier. -> For more information, see [Companion objects](object-declarations.md#companion-objects). -> -{style="tip"} \ No newline at end of file +For more information, see [Companion objects](object-declarations.md#companion-objects). \ No newline at end of file diff --git a/docs/topics/exceptions.md b/docs/topics/exceptions.md index 42749c24abd..9eef5e2ecc9 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-instances), and throwing one creates an instance of an exception class. You can throw an exception without any parameters: