16. Object, companion object
▷ object는 생성자 없이 객체를 만들어 냄, 출력만 있고 입력은 없음
▷ object로 선언된 객체는 최초 사용시 자동으로 생성되며 이후 코드전체에서 공용으로 사용 가능
▷ 프로그램이 종료되기 전까지 공통적으로 사용할 내용들을 묶어서 넣어 둠
▷ class처럼 객체지향으로 코딩하고 싶지만 코드내에서 단 하나만 생성되는 공용객체가 필요?
▷ singleton pattern : 객체를 실행할 때 한번만 실행
-------------------------------------------------
object CarFactory {
val cars = mutableListOf<Car>()
fun makeCar(horsePower : Int, name : String) : Car {
val car = Car(horsePower, name)
cars.add(car)
return car
}
fun carList(){
for((i, j) in cars) {
println("${i} , ${j}")
}
}
}
data class Car (val horsePower : Int, val name : String)
fun main () {
val car1 = CarFactory.makeCar(100, "sonata")
val car2 = CarFactory.makeCar(200, "santafe")
println(car1)
println(car2)
println("${car2.horsePower} , ${car2.name}")
println(CarFactory.cars.size.toString())
println(CarFactory.carList())
}
-------------------------------------------------
fun main () {
println(Counter.a)
Counter.countUp()
Counter.countUp()
println(Counter.a)
Counter.clear()
println(Counter.a)
var c = FoodPoll("짜장")
var d = FoodPoll("짬봉")
c.vote()
c.vote()
d.vote()
println("짜장 : ${c.count}")
println("짬뽕 : ${d.count}")
println("전체 : ${foodPoll.total}")
}
object Counter {
var a = 0
fun countUp(){
a += 4
}
fun clear() {
a = 0
}
}
// 짜장, 짬뽕 중 좋아하는 것은 ???
class FoodPoll (val name: String) {
companion object {
var total = 0
}
var count = 0
fun vote() {
total++
count++
}
}
▷ Companion Object : 기존 class 안에도 object를 만들 수 있음
· class의 속성(instance)은 그대로 사용하면서 instance의 공용 속성 및 함수를 뽑아서 만듦
· private property or method를 읽어 올수 있도록...
-------------------------------------------------
class Book private constructor (val id: Int, val name: String) {
companion object BookFac : IdProvider { //이름을 적으면 사용시에도 이름을 써야함
override fun getId() : Int {
return 45
}
val 아이디 = 33
val 이름 = "코트린 책"
fun create() = Book(아이디, 이름)
}
}
interface IdProvider {
fun getId() : Int
}
fun main() {
val book = Book.create() // val book = Book.Companion.create()
println("${book.id} : ${book.name}")
val bookID = Book.BookFac.getId()
println(bookID)
}
17. listener와 Anonymous Object
▷ observer : 이벤트가 일어나는 것을 감시하는 역할을 수행
· 시스템 또는 루틴에 의해서 발생하게 되는 동작들을 이벤트라고 부름
▷ 함수로 직접 요청하지 안았지만 키의 입력, 터치의 발생, 데이터의 수신 등(이벤트)가 발생 할 때 마다 즉각적으로 처리할 수 있도록 만드는 프로그래밍 패턴을 ‘옵저버 패턴’이라고 부름
· observer 패턴을 구현할 때는 2개의 class가 필요
· 이벤트를 수신하는 class(class A)
· 이벤트를 발생시커 이를 알려주는 class(class B)
class A
“함수를 불러라” (이벤트를 수신) |
class B
“event가 발생했습니다.” (이벤트의 발생 및 전달) |
A는 B를 참조할 수 있지만 B는 A를 참조할 수 없음 à 중간에 interface를 끼워넣음
class A
“그거 좋구나 종은 내가 만들어 주마” (이벤트를 수신) |
class B
“이제 클리되면 종을 울리겠습니다.” (이벤트의 발생 및 전달) |
이때 이 interface를 ‘observer’, kotlin에서는 ‘listener’라고 함
이렇게 이벤트를 넘겨주는 행위를 callback 이라고 함
-------------------------------------------------
/* 이벤트를 수신해서 출력하는 class EventPrinter
* 5의 배수 마다 알림을 보내는 class Counter
* 이 두개를 연결한 interface EventListener */
fun main () {
EventPrinter().start()
}
interface EventListener {
fun onEvent(count: Int)
}
class Counter (var listener : EventListener) {
fun count() {
for(i in 1..50) {
if (i % 5 == 0) listener.onEvent(i)
}
}
}
class EventPrinter : EventListener {
override fun onEvent(count: Int) {
print("${count} - ")
}
fun start() {
val counter = Counter(this)
/* this는 EventPrinter 객체 자신을 나타내지만 받는 쪽에서
* 'EventListenet'만 요구 했기 때문에 EventListener 구현부만 넘겨주게 됨
* 이를 객체지향의 다형성 이라고 함
* 상속받아 만들어진 클레스는 수퍼 class의 기능을 포함하여 제작되었으므로
* 수퍼class에서 정의한 부분만 따로 정의해서 넘겨줄수 있음. 나중에 좀더 설명 */
counter.count()
}
}
/* EvnentPrinter가 EventListener를 상속받지 않고
* 임시로 만든 별도의 EventListener 객체를 대신 넘겨줄수 있음
* 이를 '이름이 없는 객체'라 하여 Anonymous Object라고 함 */
class EventPrinter {
fun start() {
val counter = Counter(object: EventListener {
override fun onEvent(count: Int){
print("${count} /")
}
})
counter.count()
}
//이렇게 만들면 interface를 구현한 객체를 코드 중간에도 즉시 생성하여 사용할 수 있음
}
18. class의 다형성
▷ 콜라는 콜라 자체로도 볼수 있니만 음료로도 보는 것이 다형성
▷ class Cola가 class Drink를 상속받으면
· instance of Cola네는 Drink 객체 공간과 Cola의 추가 공간이 생성
instance of Cola
|
Drink의 객체 공간
|
Cola의 추가 공간
|
var a : Drink = Cola() // Drink의 객체 공간만 사용
var b : Cola = Cola() // 모두 사용
▷ 하위 class(Cola)를 상위 자료형인 수퍼 class로 변환하는 것을 Up-Casting
▷ Up-Casting된 instance를 다시 하위 자료형으로 변환하는 것을 Down-Casting
· Up-Casting은 상위 자료형에 담으면 되지만
· Down-Casting은 별도의 연산자가 필요(as , is)
- as는 변수를 호환되는 자료형으로 변환해 주는 casting 연산자로 코드 내에서 사용할 경우
var a : Drink = Cola()
a as Cola // 즉시 자료형으로 변환해 주며
var b = a as Cola //변환된 결과를 반환 받아 변수에 넣을 수 있음
- is는 변수가 자료형에 호환되는 지를 확인 후 변환 해주는 캐스팅 연산자로 조건문 내에서 사용
var a : Drink = Cola()
if(a is Cola){ “이 안에서만 a가 Cola가 됨” }
----------------------------------------
fun main () {
var a = Drink()
a.drink()
var b : Drink = Cola()
b.drink()
if (b is Cola){
b.washDishes()
}
var c = b as Cola
c.washDishes()
b.washDishes()
}
open class Drink {
var name = "음료"
open fun drink() {
println("${name}를 마십니다.")
}
}
class Cola : Drink() {
var type = "콜라"
override fun drink() {
println("${name} 중에 ${type}을 마십니다.")
}
fun washDishes(){
println("${type}으로 설거지를 하자.")
}
}
-------------------------------------------------
다형성은 class의 상속관계에서 오는 instance의 호환성을 적극 활용할 수 있는 기능으로 수퍼class가 같은 instance를 한번에 관리하거나 interface를 구현하여 사용하는 코드에서도 이용되니 이해가 꼭 필요합니다.
19. Generic
▷ class나 함수에서 사용하는 자료형을 외부에서 지정할 수 있는 기능
▷ class A를 상속받은 class B : 이 두 class의 instance를 공용으로 사용하는 하나의 함수에 패러미터로 받으려면 ?
· fun castingEx(var a: A) B는 자동으로 A에 캐스팅되면서 두 class 모두 함수의 패러미터로 사용할 수 있으나 캐스팅 연산을 거치는 것은 프로그램의 속도를 저하 시키는 단점이 있음
· 그래서 Generic이라는 개념이 나옴
▷ Generic은 함수나 클래서를 선언 할 때 고정적인 자료형 대신 실제 자료형으로 대체되는 타입 패러미터를 받아 사용하는 방법.
· 타입 파라미터에 특정 자료형이 할당 되면 제너릭을 사용하는 모든 코드는 할당된 자료형으로 대체되어 컴파일 됨 따라서 캐스팅 연산없이도 자료형을 그대로 사용할 수 있음
fun <T> genericFunc(param: T):T
class GenericClass <T> (var pref:T)
fun <Int> genericFunc(param: T):T
class GenericClass <Int> (var pref:T)
· 타입 파라미터의 이름은 class 이름과 규칙이 같지만 일반적으로 ‘Type’의 이니셜인 T를 사용하는 것이 관례. 여러 개의 제너릭을 사용할 경우 <T, U, V>를 추가적으로 사용하기도 함
· 또한 제너릭을 특정한 수퍼 class를 상속받는 class 타입으로만 제한하려면 T를 쓰고 수퍼 class이름을 명시하면됨<T:SuperClass>
· 이렇게 선언된 제너릭 사용법
fun <T> genericFunc<var param:T) { }
genericFunc(1)
-------------------------------------------------
fun main () {
UsingGeneric<A>(A()).doShouting()
UsingGeneric(B()).doShouting()
UsingGeneric(C()).doShouting()
doShouting(B())
}
fun <T: A> doShouting(t: T) {
t.shout()
}
open class A {
open fun shout() {
println("A가 소리침..")
}
}
class B : A() {
override fun shout() {
println("B가 소리침***")
}
}
class C : A() {
override fun shout() {
println("C가 소리침***")
}
}
class UsingGeneric<T: A> (val t: T) {
fun doShouting() {
t.shout()
}
}
'kotlin' 카테고리의 다른 글
Kotlin String (0) | 2023.01.14 |
---|---|
Kotlin List (0) | 2023.01.14 |
Kotlin class-02 (0) | 2023.01.14 |
Kotlin class-01 (0) | 2023.01.14 |
Kotlin 기본 형, loop ,if (0) | 2023.01.14 |