Kotlinμ 곡λΆνλ©΄μ μμμ λν λΆλΆμ 곡λΆνκ³ μ 곡μ λ¬Έμλ₯Ό μ§μ λ²μνλ€.
ν΄λΉ λ¬Έμλ 2022λ
3μ 2μΌμ 곡μ λ¬Έμμ΄λ€.
Kotlin 곡μλ¬Έμ νμΈνκΈ° β‘οΈ
β» μμ΄ μ 곡μλ ν΄μΈ μ ννλ μλκΈ°μ λ²μμλ μμ, μ€μ, κ΅¬κΈ λ²μμ΄ λ¬΄μν λ§μ μ μμΌλ©°, νΌμ 곡μλ¬Έμλ₯Ό μ°Έμ‘°ν΄κ°λ©° λ²μνλ€ λ³΄λ μ€νλ λ§μ μ μλ€. μ νν λ΄μ©μ 곡μλ¬Έμλ₯Ό μ§μ μ΄ν΄λ³΄κ±°λ λ€λ₯Έ μ 보λ€μ λ μ°Ύμ보λ κ²μ μΆμ²νλ€.
(νμ§λ§ λκΈ νΌλλ°±λ νμν©λλ€π )
All classes in Kotlin have a common superclass, Any, which is the default superclass for a class with no supertypes declared:
μ½νλ¦°μ μλ λͺ¨λ ν΄λμ€μλ κ³΅ν΅ μνΌν΄λμ€ Anyκ° μλλ°, μ΄κ²μ μ μΈλ μνΌνμ μ΄ μλ ν΄λμ€μ κΈ°λ³Έ μνΌν΄λμ€μ΄λ€.
Any has three methods: equals(), hashCode(), and toString(). Thus, these methods are defined for all Kotlin classes.
Anyλ 3κ°μ§ λ©μλλ₯Ό κ°κ³ μλ€: equals(), hashCode(), toString(). κ·Έλ¬λ―λ‘ μ΄ λ©μλλ€μ λͺ¨λ μ½νλ¦° ν΄λμ€λ€μλ μ μλμ΄ μλ€.
By default, Kotlin classes are final – they can’t be inherited. To make a class inheritable, mark it with the open keyword:
κΈ°λ³Έκ°μΌλ‘ μ½νλ¦° ν΄λμ€λ€μ finalμ΄λ€ μ¦ μμλ μ μλ€. ν΄λμ€λ₯Ό μμ κ°λ₯νκ² λ§λ€λ €λ©΄ open ν€μλλ‘ νμλ₯Ό ν΄μ£Όμ΄μΌνλ€:
To declare an explicit supertype, place the type after a colon in the class header:
λͺ μμ μΈ μνΌνμ μ μ μΈνλ €λ©΄ λͺ μνλ €λ κ·Έ νμ μ class ν€λμ μ½λ‘ (:) λ€μ λμ΄λΌ:
open class Base(p: Int)
class Derived(p: Int) : Base(p)
If the derived class has a primary constructor, the base class can (and must) be initialized in that primary constructor according to its parameters.
λ§μ½ νμλ ν΄λμ€κ° κΈ°λ³Έ(primary) μμ±μλ₯Ό κ°μ§κ³ μλ€λ©΄ κΈ°λ³Έ(base) ν΄λμ€λ κΈ°λ³Έ μμ±μμμ νλΌλ―Έν°μ λ°λΌ μ΄κΈ°ν λ μ (μ΄κΈ°ν λμ΄μΌ νλ€)μλ€.
If the derived class has no primary constructor, then each secondary constructor has to initialize the base type using the super keyword or it has to delegate to another constructor which does. Note that in this case different secondary constructors can call different constructors of the base type:
λ§μ½ νμ ν΄λμ€μ κΈ°λ³Έ(primary) μμ±μκ° μλ€λ©΄, κ° λ³΄μ‘°(secondary) μμ±μλ κΈ°λ³Έ νμ μ super ν€μλλ₯Ό μ¬μ©νμ¬ μ΄κΈ°ν νκ±°λ κ·Έλ κ² νλ λ€λ₯Έ μμ±μμ μμν΄μΌνλ€. μ΄ κ²½μ° λ€λ₯Έ 보쑰 μμ±μκ° κΈ°λ³Έ νμ μ λ€λ₯Έ μμ±μλ₯Ό νΈμΆν μ μλ€:
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
Overriding methods
Kotlin requires explicit modifiers for overridable members and overrides:
μ½νλ¦°μ μ€λ²λΌμ΄λ© κ°λ₯ν λ©€λ²μ μ€λ²λΌμ΄λ ν¨μμ λν΄ λͺ μμ μΈ μμμ΄κ° μꡬ λλ€:
open class Shape {
open fun draw() { /*...*/ }
fun fill() { /*...*/ }
}
class Circle() : Shape() {
override fun draw() { /*...*/ }
}
The override modifier is required for Circle.draw(). If it were missing, the compiler would complain. If there is no open modifier on a function, like Shape.fill(), declaring a method with the same signature in a subclass is not allowed, either with override or without it. The open modifier has no effect when added to members of a final class – a class without an open modifier.
μ€λ²λΌμ΄λ μμμ΄λ Circle.draw()μ μꡬ λλ€. λ§μ½ μ΄ μμμ΄κ° μμΌλ©΄, μ»΄νμΌλ¬κ° μ΄κ²μ λν΄ λΆνν κ²μ΄λ€. (νκ΅μ΄λ‘λ μ΄μν ννμ΄μ§λ§;;) Shape.fill()κ³Ό κ°μ΄ open μμμ΄κ° ν¨μ μμ μμΌλ©΄ override μμμ΄κ° μλ μλ μλΈν΄λμ€μμ λμΌν signatureλ₯Ό κ°κ³ μλ λ©μλλ₯Ό μ μνλ κ²μ νμ©λμ§ μλλ€. open μμμ΄λ final ν΄λμ€ - open μμμ΄κ° μλ ν΄λμ€μ λ©€λ²μκ² λΆμ¬μ Έλ μ무 ν¨λ ₯μ΄ μλ€.
A member marked override is itself open, so it may be overridden in subclasses. If you want to prohibit re-overriding, use final:
overrideλ‘ νμλ λ©€λ² μμ²΄κ° μ΄λ €(open)μκΈ° λλ¬Έμ μλΈν΄λμ€μμ μ€λ²λΌμ΄λ λ μ μλ€. μ¬-μ€λ²λΌμ΄λ©μ κΈμ§νκ³ μΆλ€λ©΄ finalμ μ¬μ©νλΌ:
Overriding properties
The overriding mechanism works on properties in the same way that it does on methods. Properties declared on a superclass that are then redeclared on a derived class must be prefaced with override, and they must have a compatible type. Each declared property can be overridden by a property with an initializer or by a property with a get method:
μ€λ²λΌμ΄λ© λ©μ»€λμ¦μ λ©μλμ μ μ©λ κ²κ³Ό κ°μ λ°©μμΌλ‘ μμ±λ€μλ μ μ©λλ€. μνΌν΄λμ€μμ μ μΈλ ν νμν΄λμ€μμ μ¬μ μΈλ μμ±μ overrideλ‘ μμν΄μΌνκ³ νΈν κ°λ₯ν νμ μ΄ μμ΄μΌνλ€. κ° μ μΈλ μμ±μ initializerκ° μλ μμ±μ΄λ get λ©μλκ° μλ μμ±μ μν΄ μ€λ²λΌμ΄λ λ μ μλ€:
open class Shape {
open val vertexCount: Int = 0
}
class Rectangle : Shape() {
override val vertexCount = 4
}
You can also override a val property with a var property, but not vice versa. This is allowed because a val property essentially declares a get method, and overriding it as a var additionally declares a set method in the derived class.
val μμ±μ var μμ±μΌλ‘ μ€λ²λΌμ΄λν μ μμ§λ§ λ°λμ κ²½μ°λ λΆκ°λ₯νλ€. π μ΄κ²μ΄ νμ©λλ μ΄μ λ val μμ±μ΄ λ³Έμ§μ μΌλ‘ get λ©μλλ₯Ό μ μΈνκ³ μ΄κ²μ varλ‘ μ€λ²λΌμ΄λνλ©΄ νμν΄λμ€μμ set λ©μλλ₯Ό μΆκ° μ μΈνκΈ° λλ¬Έμ΄λ€.
Note that you can use the override keyword as part of the property declaration in a primary constructor:
κΈ°λ³Έ μμ±μμμ μμ± μ μΈμ μΌλΆλΆμΌλ‘ override ν€μλλ₯Ό μ¬μ©ν μ μλ€:
interface Shape {
val vertexCount: Int
}
class Rectangle(override val vertexCount: Int = 4) : Shape // Always has 4 vertices
class Polygon : Shape {
override var vertexCount: Int = 0 // Can be set to any number later
}
Derived class initialization order
During the construction of a new instance of a derived class, the base class initialization is done as the first step (preceded only by evaluation of the arguments for the base class constructor), which means that it happens before the initialization logic of the derived class is run.
νμν΄λμ€μ μλ‘μ΄ μΈμ€ν΄μ€λ₯Ό μμ±νλ λμ, κΈ°λ³Έν΄λμ€μ μ΄κΈ°νλ 첫 λ²μ§Έλ‘(κΈ°λ³Έν΄λμ€ μμ±μμ λν μΈμ νκ°μ μν΄μλ§ μ νλ¨) μ΄λ£¨μ΄μ§λλ°, μ΄κ²μ μλ―Έλ νμν΄λμ€μ μ΄κΈ°ν λ‘μ§μ΄ μ€νλκΈ° μ μ ν΄λΉ λμμ΄ μ΄λ£¨μ΄μ§λ€λ κ²μ΄λ€.
open class Base(val name: String) {
init { println("Initializing a base class") }
open val size: Int =
name.length.also { println("Initializing size in the base class: $it") }
}
class Derived(
name: String,
val lastName: String,
) : Base(name.replaceFirstChar { it.uppercase() }.also { println("Argument for the base class: $it") }) {
init { println("Initializing a derived class") }
override val size: Int =
(super.size + lastName.length).also { println("Initializing size in the derived class: $it") }
}
fun main() {
println("Constructing the derived class(\"hello\", \"world\")")
Derived("hello", "world")
}
Constructing the derived class("hello", "world")
Argument for the base class: Hello
Initializing a base class
Initializing size in the base class: 5
Initializing a derived class
Initializing size in the derived class: 10β
This means that when the base class constructor is executed, the properties declared or overridden in the derived class have not yet been initialized. Using any of those properties in the base class initialization logic (either directly or indirectly through another overridden open member implementation) may lead to incorrect behavior or a runtime failure. When designing a base class, you should therefore avoid using open members in the constructors, property initializers, or init blocks.
μ΄ μλ―Έλ κΈ°λ³Έ ν΄λμ€ μμ±μκ° μ€νλμμ λ νμν΄λμ€μ μλ μ μΈλ μμ±λ€μ΄λ μ€λ²λΌμ΄λ λ κ²λ€μ΄ μμ§ μ΄κΈ°ν λμ§ μμλ€λ κ²μ΄λ€. κΈ°λ³Έν΄λμ€ μ΄κΈ°ν λ‘μ§μμ (μ§μ μ μΌλ‘λ μ€λ²λΌμ΄λλ λ€λ₯Έ open λ©€λ² κ΅¬νμ ν΅ν΄ κ°μ μ μΌλ‘λ ) μ΄λ¬ν μμ±λ€ μ€ μ΄λ κ²μ΄λ μ¬μ©νλ κ²μ μλͺ»λ λμμ΄λ λ°νμ μ€λ₯λ₯Ό μ΄λν μ μλ€. κ·Έλ¬λ―λ‘ κΈ°λ³Έν΄λμ€λ₯Ό μ€κ³ν λ μμ±μ, μμ±, initializer νΉμ μ΄κΈ°ν λΈλμμ open λ©€λ²λ₯Ό μ¬μ©νλ κ²μ νΌν΄μΌνλ€.
Calling the superclass implementation
Code in a derived class can call its superclass functions and property accessor implementations using the super keyword:
νμν΄λμ€μ μ½λλ super ν€μλλ₯Ό μ¬μ©νμ¬ μνΌν΄λμ€μ ν¨μμ μμ± μ κ·Όμ ꡬνμ νΈμΆν μ μλ€:
open class Rectangle {
open fun draw() { println("Drawing a rectangle") }
val borderColor: String get() = "black"
}
class FilledRectangle : Rectangle() {
override fun draw() {
super.draw()
println("Filling the rectangle")
}
val fillColor: String get() = super.borderColor
}
Inside an inner class, accessing the superclass of the outer class is done using the super keyword qualified with the outer class name: super@Outer:
λ΄λΆ ν΄λμ€ μμμ μΈλΆ ν΄λμ€μ μνΌν΄λμ€μ μ κ·Όνλ κ²μ super ν€μλμ μΈλΆ ν΄λμ€ μ΄λ¦μ μ¬μ©νλ©΄ κ°λ₯νλ€: super@Outer:
open class Rectangle {
open fun draw() { println("Drawing a rectangle") }
val borderColor: String get() = "black"
}
class FilledRectangle: Rectangle() {
override fun draw() {
val filler = Filler()
filler.drawAndFill()
}
inner class Filler {
fun fill() { println("Filling") }
fun drawAndFill() {
super@FilledRectangle.draw() // Calls Rectangle's implementation of draw()
fill()
println("Drawn a filled rectangle with color ${super@FilledRectangle.borderColor}") // Uses Rectangle's implementation of borderColor's get()
}
}
}
fun main() {
val fr = FilledRectangle()
fr.draw()
}
Drawing a rectangle
Filling
Drawn a filled rectangle with color black
Overriding rules
In Kotlin, implementation inheritance is regulated by the following rule: if a class inherits multiple implementations of the same member from its immediate superclasses, it must override this member and provide its own implementation (perhaps, using one of the inherited ones).
μ½νλ¦°μμ μμ ꡬνμ λ€μμ κ·μΉλ€λ‘ κ·μ λλ€: ν΄λμ€κ° μ§κ³ μνΌν΄λμ€λ‘λΆν° λμΌν λ©€λ²μ μ¬λ¬ ꡬνμ μμνλ κ²½μ°, ν΄λμ€λ λ°λμ μ΄ λ©€λ²λ₯Ό μ€λ²λΌμ΄λνκ³ κ³ μ ν ꡬνμ μ 곡ν΄μΌνλ€(μλ§λ μμλ κ² μ€ νλλ₯Ό μ¬μ©νμ¬μ).
To denote the supertype from which the inherited implementation is taken, use super qualified by the supertype name in angle brackets, such as super<Base>:
μμλ ꡬνμ΄ μ΄λμλΆν° μ¨ κ²μΈμ§ μνΌνμ μ νμνκΈ° μν΄μλ super<Base>μ κ°μ΄ κΊΎμ κ΄νΈ μμ μνΌνμ μ΄λ¦μΌλ‘ νμ λλ superλ₯Ό μ¬μ©νλ€:
open class Rectangle {
open fun draw() { /* ... */ }
}
interface Polygon {
fun draw() { /* ... */ } // interface members are 'open' by default
}
class Square() : Rectangle(), Polygon {
// The compiler requires draw() to be overridden:
override fun draw() {
super<Rectangle>.draw() // call to Rectangle.draw()
super<Polygon>.draw() // call to Polygon.draw()
}
}
It's fine to inherit from both Rectangle and Polygon, but both of them have their implementations of draw(), so you need to override draw() in Square and provide a separate implementation for it to eliminate the ambiguity.
Rectangleκ³Ό Polygon λͺ¨λλ‘λΆν° μμλ°λ κ²μ λ¬Έμ μμ§λ§ λλ€ draw()μ΄ κ΅¬νλμ΄ μκΈ° λλ¬Έμ Squareμμ draw()λ₯Ό μ€λ²λΌμ΄λν΄μΌνκ³ λͺ¨νΈμ±μ μμ κΈ° μν΄ λ³λμ ꡬνμ μ 곡ν΄μΌνλ€.
'Android & Kotlin' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
[λ΄λ³΄λ΄λ²] Properties | Kotin (0) | 2022.03.03 |
---|---|
[λ΄λ³΄λ΄λ²] Classes | Kotlin (0) | 2022.03.03 |
Overloading / Overriding (0) | 2022.03.02 |
Lesson 6: App Architecture (Persistence) (0) | 2022.02.23 |
Lesson 5: App Architecture(UI Layer) (0) | 2022.02.22 |