Skip to content

Commit

Permalink
doc(docs.topics.concepts.classAndObjects.objectExpressionAndDeclarati…
Browse files Browse the repository at this point in the history
…ons.companionObjects): add notes and examples
  • Loading branch information
alfredo-toledano committed Aug 27, 2024
1 parent 83a7c39 commit 348c9ba
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
* 👀Ways to create an object / slight modification of some class, without declaring explicitly a new subclass 👀
* object expressions
* object declarations

# Object expressions
* TODO:

Expand All @@ -16,8 +12,7 @@
* TODO:
## Data objects
* TODO:
## Companion objects
* TODO:


# Code Examples
* Mirror the examples added, to run it locally
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
[//]: # (title: Object expressions and declarations)

Sometimes you need to create an object that is a slight modification of some class, without explicitly declaring a new
subclass for it. Kotlin can handle this with _object expressions_ and _object declarations_.
* 👀Ways to create an object / slight modification of some class & NOT new subclass 👀
* object expressions
* object declarations

## Object expressions

* TODO:
_Object expressions_ create objects of anonymous classes, that is, classes that aren't explicitly declared with the `class`
declaration. Such classes are useful for one-time use. You can define them from scratch, inherit from existing classes,
or implement interfaces. Instances of anonymous classes are also called _anonymous objects_ because they are defined by
Expand Down Expand Up @@ -284,71 +286,34 @@ fun main() {

### Companion objects

An object declaration inside a class can be marked with the `companion` keyword:
* := object declaration | class / marked with `companion`

```kotlin
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
```
class ClassName {
companion object ObjectName {
CompanionObjectMembers
}
classMembers
}
}
```

Members of the companion object can be called simply by using the class name as the qualifier:

```kotlin
val instance = MyClass.create()
```

The name of the companion object can be omitted, in which case the name `Companion` will be used:

```kotlin
class MyClass {
companion object { }
}

val x = MyClass.Companion
```

Class members can access the private members of the corresponding companion object.

The name of a class used by itself (not as a qualifier to another name) acts as a reference to the companion
object of the class (whether named or not):

```kotlin
class MyClass1 {
companion object Named { }
}

val x = MyClass1

class MyClass2 {
companion object { }
}

val y = MyClass2
```

Note that even though the members of companion objects look like static members in other languages, at runtime those
are still instance members of real objects, and can, for example, implement interfaces:

```kotlin
interface Factory<T> {
fun create(): T
}

class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}

val f: Factory<MyClass> = MyClass
```

However, on the JVM you can have members of companion objects generated as real static methods and fields if you use
the `@JvmStatic` annotation. See the [Java interoperability](java-to-kotlin-interop.md#static-fields) section
for more detail.
```
* ways to refer to `companion` object
* ⚠️if ObjectName (name of the companion object) is omitted -> `ClassName.Companion` ⚠️
* ⚠️if ObjectName is declared -> `ClassName.ObjectName` ⚠️
* `ClassName.Companion` NOT valid here
* 👁️1! companion object allowed / class 👁️
* `classMembers` -- can access to -- private `CompanionObjectMembers`
* `ClassName` is used as reference to the companion object -- `print(companionObject)`--
* `CompanionObjectMembers`
* seem statics
* ⚠️ are instances of real objects | runtime ⚠️
* == own state & behavior + implement interfaces
* == companion objects == objects
* `ClassName.CompanionObjectMember`
* way to call companion object’s members
* ONLY valid if `CompanionObjectMember` is public
* 👁️!= `instanceClassName.CompanionObjectMember`, does NOT work 👁️
* `@JvmStatic CompanionObjectMembers` -> real static members | JVM
* Check [Java interoperability](java-to-kotlin-interop.md#static-fields)
### Semantic difference between object expressions and declarations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,73 @@ object ObjectDeclarationWithSuperTypes : MyMouseAdapter() {
}
}*/

// 4. companion objects
class MyClass {
// NO objectname for the companion
companion object {
var number = 3
private val privateNumber = 4
}

// class member can get access to a private member of a companion object
fun sampleFunction() {
println(privateNumber)
}
// ONLY allowed 1! companion object / class -> Next companion object declaration throws an erro
/*companion object another {
val anotherNumber = 4
}*/
}
class AnotherClass {
// name for the companion
companion object nameToCompanionObject{
var number = 3
}
}
// 4.4 companion objects | runtime == real objects
interface Greeter {
fun greet(): String
}

class ClassCheckCompanionObjectsAreLikeObjects {
companion object : Greeter { // companion object / NO name, implementing an interface
override fun greet(): String {
return "Hello from the companion object!"
}
fun show() {
println("This is a companion object method.")
}
}
}

fun main() {
// 2. Initialize an object declaration
CounterObject.incrementCounter()
var objectInitialized = CounterObject.getCounter()
println("objectInitialized::class.simpleName ${objectInitialized::class.simpleName} whose value is $objectInitialized")


// 4. companion object
val myClass = MyClass()
// 4.1 `instanceClassName.CompanionObjectMember` NOT valid
/*println("myClass.privateNumber ${myClass.privateNumber}")
println("myClass.number ${myClass.number}")*/
// 4.2 `ClassName.CompanionObjectMember` just if member is public
println("MyClass.number ${MyClass.number}")
//println("MyClass.privateNumber ${MyClass.privateNumber}") NOT valid because it's private
// 4.3 companion object / name omitted -> refer to it via `ClassName.Companion`
val companionReference = MyClass.Companion
println("companionReference ${companionReference}") // ClassName is print as reference to the companion object
println("companionReference.number ${companionReference.number}")
// companion object / name -> refer to it via it's name
val anotherCompanionReferenceByName = AnotherClass.nameToCompanionObject
println("anotherCompanionReferenceByName ${anotherCompanionReferenceByName}") // ClassName is print as reference to the companion object
// val anotherCompanionReference = AnotherClass.Companion NOT valid to refer by Companion
//println("anotherCompanionReference ${anotherCompanionReference}")
// 4.4 companion objects | runtime == objects
// reference of type Greeter / -- assigned to -- ClassCheckCompanionObjectsAreLikeObjects == reference to the companion object
val greeter: Greeter = ClassCheckCompanionObjectsAreLikeObjects
println(greeter.greet()) // call from this reference to the method -> proving companion object is a real object

// Checking the runtime type
println("Is companion object an instance of Greeter? ${ClassCheckCompanionObjectsAreLikeObjects is Greeter}")
}

0 comments on commit 348c9ba

Please sign in to comment.