- open 키워드와 상속
다른 클래스를 상속하는 방법은
현재클래스이름(생성자) : 상속클래스이름(생성자)
코틀린은 default 로 부모 클래스의 properties 와 member variables 를 private 이 아닌 이상 접근은 가능하지만 override 할 수 없다.
자식클래스가 override 할 수 있게 하려면, 부모클래스에서 open 키워드를 사용해야한다.
클래스는 물론 생성자에 있는 멤버변수 모두에 각각 open 키워드를 붙이고 하위클래스에서 override 할 수 있다.
open class Aquarium constructor(open var width: Int = 20, open var height: Int = 40, open var length: Int = 100) {
open var water: Double = 0.0
get() = volume * 0.9
open val shape = "rectangle"
open var volume: Int
get() = width * height * length / 1000
set(value) {
height = (value * 1000) / (width * length)
}
fun printSize() {
println(shape)
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm ")
// 1 l = 1000 cm^3
println("Volume: $volume l Water: $water l (${water/volume*100.0}% full)")
}
}
class TowerTank (override var height: Int, var diameter: Int)
: Aquarium(height = height, width = diameter, length = diameter) {
override var water = volume * 0.8
override val shape = "cylinder"
override var volume: Int
// ellipse area = π * r1 * r2
get() = (width/2 * length/2 * height / 1000 * PI).toInt()
set(value) {
height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
}
}
Aquarium 클래스를 TowerTank 클래스가 상속하고 있다.
package aquarium
fun buildAquarium() {
val aquarium6 = Aquarium(length = 25, width = 25, height = 40)
aquarium6.printSize()
val myTower = TowerTank(diameter = 25, height = 40)
myTower.printSize()
}
fun main() {
buildAquarium()
}
그리고 위와같이 실행하면 결과는 아래와 같다.
rectangle
Width: 25 cm Length: 25 cm Height: 40 cm
Volume: 25 l Water: 22.5 l (90.0% full)
cylinder
Width: 25 cm Length: 25 cm Height: 40 cm
Volume: 18 l Water: 14.4 l (80.0% full)
- interface vs abstract class
abstract class AquariumFish {
abstract val color: String
}
class Shark: AquariumFish(), FishAction {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
class Plecostomus: AquariumFish(), FishAction {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
interface FishAction {
fun eat()
}
우선 abstract class 에 대해 알아보자
abstract class 는 항상 open 이다. 그래서 따로 open 키워드를 써줄 필요가 없다. 추상클래스 안의 멤버들도 따로 다 abstract 키워드를 붙이면 open 을 따로 적을 필요가 없다.
abstract 키워드가 붙으면 그 추상클래스를 상속한 하위클래스는 반드시 그 멤버를 implement 해야한다.
이제 interface 와 비교해보자
일단 공통점은 interface 와 abstract class 는 하위클래스에 가져와서 구현한다는 흐름을 가진다는 것이다. 즉 공통적인 것? 들을 묶어 공유한다는 점에서는 비슷하다. 당연히 두가지 모두 바로 객체를 생성할 수 없다는 점도 그렇다.
차이점은 interface 는 생성자를 가질 수 없는 반면, abstract class 는 생성자를 가질 수 있다.
또 차이점은 구현클래스는 하나의 abstract class 밖에 구현하지 못한다는 점이다. interface 는 여러개를 받아와서 구현할 수 있다.
참고로 abstract class 에서 interface 를 구현해주는 방법도 있다.
- singleton class ( object 키워드 ) 와 delegation 개념
object 키워드가 붙은 클래스는 코틀린이 딱 하나의 객체를 만들고 전역에서 obect 클래스의 이름으로 해당 클래스를 참조할 수 있게 해준다.
이것도 interface delegation 으로 사용할 수 있다. 구현클래스에서 ` : interface이름` 으로 인터페이스를 가져오고 뒤에 `by object클래스이름` 을 써주면된다. 여기서 interface delegation 이란 인터페이스를 대표로 구현한 대표클래스(delegate 대표 뜻이 대리자)를 말하는 것 같다. `: 인터페이스이름 by delegation클래스이름` 으로 써주면 된다. 아래 코드에서 Plecostomus 클래스를 집중적으로 참고한다.
object 클래스와 그냥 class 를 interface delegation 으로 사용하였다.
package aquarium
class Shark: FishAction, FishColor {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
class Plecostomus(fishColor: FishColor = GoldColor) :
FishAction by PrintingFishAction("eagle"),
FishColor by fishColor
interface FishAction {
fun eat()
}
interface FishColor {
val color: String
}
object GoldColor : FishColor {
override val color = "gold"
}
class PrintingFishAction(private val food: String = "bob"): FishAction {
override fun eat() {
println("eat $food")
}
}
이렇게 interface delegation 을 사용함으로써 많은 하위클래스를 만드는 것을 막을 수 있다.
- data class
data class Decoration(
val rock: String
)
fun makeDecorations() {
val decoration1 = Decoration("granite")
println(decoration1)
val decoration2 = Decoration("slate")
println(decoration2)
val decoration3 = Decoration("slate")
println(decoration3)
println (decoration1.equals(decoration2))
println (decoration3.equals(decoration2))
println (decoration1 == decoration2)
println (decoration2 == decoration3)
println (decoration1 == decoration3)
}
makeDecorations 을 한번 호출했을 때 결과는 아래와같다.
Decoration(rock=granite)
Decoration(rock=slate)
Decoration(rock=slate)
false
true
false
true
false
우선 equals 를 오버라이딩하지 않았는데도 자동으로 생성되었음을 확인할 수 있다.
자바에서는 마지막에서 두번째 줄도 true 가 아닌 false 가 나올텐데 코틀린은 true 가 나오는 것을 확인할 수 있다. 즉 코틀린에서 같은 data class 객체끼리의 == 연산은 equals 와 같은 것이다. 영어로는 structural equality 라고 한다. contents 가 같다고도 표현한다.
두 객체가 같은 객체를 참조하고 있는지 확인하려면 === 연산자를 사용해야한다.
copy() 함수로 contents 를 복사한 새 객체를 만들 수 있다.
주의할 점은 copy(), equals() 와 같은 data class utility 는 오직 priamry constructor 에 정의된 properties 만 참조한다는 점이다.
data class Decoration2(val rocks: String, val wood: String, val diver: String){
}
fun makeDecorations2() {
val d5 = Decoration2("crystal", "wood", "diver")
println(d5)
// Assign all properties to variables.
val (rock, wood, diver) = d5
println(rock)
println(wood)
println(diver)
}
val (rock, wood, diver) = d5 에 주목하자 data class 객체로 변수 여러개를 한번의 = 로 처리할 수 있다. 이러한 것을 destructuring 이라고 부른다. 변수들의 개수와 순서가 data class 에 정의된 properties 와 일치하다고 생각해야한다.
참고.
디스트럭처링(Destructuring)은 구조화된 배열 또는 객체를 Destructuring(비구조화, 파괴)하여 개별적인 변수에 할당하는 것이다. (출처 : https://poiemaweb.com/es6-destructuring )
val (rock, _, diver) = d5
위와같은 방식으로 하나를 건너뛰고 두개만 받아올 수도 있다.
- enum class
다른 언어의 enum 과 비슷하다. 이름 열거해서 참조한다.
이름마다 () 안에 생성자 자리에 있는 변수값들을 써준다. 그리고 name, ordinal, degrees 프로퍼티를 사용해서 값을 가져올 수 있다.
선언과 사용방법은 아래와 같다. 두 번째 코든느 생성자위치에 변수를 두개 둬서 사용해보았다.
enum class Direction(val degrees: Int) {
NORTH(0), SOUTH(180), EAST(90), WEST(270)
}
fun main() {
println(Direction.EAST.name)
println(Direction.EAST.ordinal)
println(Direction.EAST.degrees)
}
EAST
2
90
enum class Direction(val degrees: Int, val a: Int) {
NORTH(0, 1), SOUTH(180, 181), EAST(90, 91), WEST(270, 271)
}
fun main() {
println(Direction.EAST.name)
println(Direction.EAST.ordinal)
println(Direction.EAST.degrees)
println(Direction.EAST.a)
}
EAST
2
90
91
- 출처 : codelabs
https://developer.android.com/codelabs/kotlin-bootcamp-classes
'Kotlin' 카테고리의 다른 글
[Kotlin] 람다함수는 무엇이고 왜 쓰고 어떻게 쓸까? (1) | 2023.01.01 |
---|---|
[Kotlin] Pair , Triple 과 Destructure (0) | 2021.08.05 |
[Kotlin] 접근제어자 ( visibility modifiers ) (0) | 2021.08.04 |
[Kotlin] Class (constructor, getter/setter, companion object) 기초 (0) | 2021.08.04 |
[Kotlin] lambda 와 higher-order function (0) | 2021.08.03 |