Kotlin_04

김재현·2023년 3월 26일
0

보조 프로퍼티 사용

  • 만일 보조 필드를 사용하지 않는 경우에는 임시적으로 사용할 프로퍼티를 선언해 놓고 게터나 세터에서 사용할 수 있다.

임시적인 보조 프로퍼티 사용하기보조 필드인 field를 사용하지 않고 추가로 내부의 프로퍼티를 임시로 선언했다.

프로퍼티 오버라이딩

  • 프로퍼티는 기본적으로 오버라이딩 할 수 없는 final형태로 선언된다. 만일 프로퍼티를 오버라이딩 가능하게 하려면 open 키워드를 사용해 프로퍼티를 선언해야 한다.

프로퍼티의 오버라이딩 사용하기

게터 세터 예시


지연 초기화와 위임

  • 프로퍼티를 선언하면 기본적으로 모두 초기화 해야한다. 하지만 객체의 정보가 나중에 나타나는 경우 힘든 경우가 있다.
  • 보통 클래스에서는 기본적으로 선언하는 프로퍼티 자료형들은 null을 가질 수 없기 때문에 생성자에서 초기화하거나 매개변수로부터 값을 초기화해야한다.
  • 이 경우 lateinit과 lazy 키워드를 통해 지연과 위임을 한다.

lateinit을 사용한 지연 초기화

  • var로 선언된 프로퍼티만 가능하다.
  • 프로퍼티에 대한 게터와 세터를 사용할 수 없다.

lateinit을 사용해 지연 초기화하기

객체를 생성할때도 생성자를 않넣고 선언만 할 수 있다.

data class Person(var name: String, var age: Int)

lateinit var person1: Person // 객체 생성의 지연 초기화

fun main() {
	person1 = Person("Kildong", 30) // 생성자 호출 시점에서 초기화됨
    print(person1.name + " is " + person1.age.toString())
}

lazy를 사용한 지연 초기화

  • 호출 시점에 by lazy {...}정의에 의해 블록 부분의 초기화를 진행한다.
  • 불변의 변수 선언인 val에서만 사용 가능하다.(읽기전용)
  • 람다식으로 구성되어 lazy 인스턴스 반환값을 가지는 함수이다.

by lazy로 선언된 프로퍼티 지연 초기화하기

by lazy로 객체 지연 초기화

  • by lazy나 lazy할당의 차이점은 by lazy는 객체의 위임을 나타내며 lazy는 변수에 위임된 lazy 객체를 나타내므로 이 변수의 value를 한 단계 더 거쳐 객체의 멤버인 value.name과 같은 형태로 접근해야 한다는 것이다.

by를 이용한 위임

  • 하나의 클래스가 다른 클래스에 위임하도록 선언하여 위임된 클래스가 가지는 멤버를 참조 없이 호출할 수 있다.
  • 프로퍼티 위임으로 프로퍼티의 게터와 세터를 특정 객체에게 위임하고 그 객체가 값을 읽거나 쓸 때 수행하도록 만드는것.
< val|var|class > 프로퍼티 혹은 클래스 이름: 자료형 by 위임자
  • 표준 라이브러리가 open으로 정의되어 있지 않은 경우가 많기 때문에, 필요한경우에만 위임을 통해 상속과 비슷하게 해당 클래스의 모든 기능을 사용하면서 동시에 기능을 추가 확장 구현할 수 있다.

클래스 위임 사용하기


observable() 함수와 vetoable()함수의 위임

  • observable() : 프로퍼티를 감시하고 있다가 특정 코드의 로직에서 변경이 일어날 때 호출되어(콜백) 처리한다.
  • vetoable() : observable()과 비슷하지만 반환값에 따라 프로퍼티 변경을 허용하거나 취소할 수 있다는 점에서 다르다.
  • 매개변수에 기본값을 지정해야한다.

observable() 함수 간단히 사용해보기

vetoable() 함수를 사용한 최대값 구하기

vetoable()함수는 컬렉션과 같이 큰 데이터를 다룰때 유용하다.


6-3 정적 변수와 컴패니언 객체

  • 동적인 메모리에 할당 해제되는 것이 아닌, 프로그램을 실행할 때 고정적으로 가지는 메모리로 객체 생성 없이 사용할 수 있다.

컴페니언 객체 사용하기

  • 코틀린에서는 static 키워드 대신 컴패니언 객체를 제공한다.

컴패니언 객체 사용해보기

컴패니언 객체는 실제 객체의 싱글톤으로 정의된다.

싱글톤이란?

  • 전역 변수를 사용하지 않고 객체를 하나만 생성하도록 하며, 생성된 객체를 어디서든지 참조할 수 있도록 하는 디자인의 패턴의 하나.
  • 객체가 서로 동일한 정보를 가질 때 하나의 메모리만 유지해 자원의 낭비를 줄일 수 있다.

자바에서 컴패니언 객체를 가진 코틀린의 클래스에 접근하도록 하기

  • kotlin에서 @JvmStatic으로 어노테이션 표기 사용해주기.
  • 어노테이션을 사용하지 않을 경우 KCustomer.Companion.login();과 같이 Companion을 포함하여 java에서 작성한다.

프로퍼티를 자바에서 사용하고자 할 경우

  • @JvmField어노테이션으 사용하면 된다.

object와 싱글톤

내용이 조금 변경된 클래스를 만들때. 새로 하위 클래스를 선언하지 않고 조금 변경한 객체를 생성하고 싶을때.
자바의 경우 익명 내부 클래스를 사용해 새로운 클래스 선언을 피할 수 있지만 코틀린은 object 표현식이나 object 선언으로 조 더 쉽게 처리할 수 있다.

object 선언과 컴패니언 객체 비교하기

  • object로 선언된 OCustomer는 멤버 프로퍼티와 메서드를 객체 생성 없이 이름의 점 표기법으로 바로 사용할 수 있다.(싱글톤)
  • object 선언 방식을 사용하면 접근 시점에 객체가 생성된다. 그렇기 때문에 생성자 호출을 하지 않으므로 object 선언에는 주 생성자와 부 생성자를 사용할 수 없다. 하지만 초기화 블록인 init이 들어갈 수 있는데 최초 접근에서 실행된다.
  • object 선언에서도 클래스나 인터페이스를 상속할 수 있다.
  • 만일 자바에서 object 선언으로 생성된 인스턴스에 접근하려면 INSTANCE를 사용한다.

object 표현식

  • object 표현식은 object 선언과 달리 이름이 없으며 싱글톤이 아니다.
  • 따라서 object 표현식이 사용될 때마다 새로운 인스턴스가 생성된다.
  • 결과적으로 이름이 없는 익명 내부 클래스로 불리는 형태를 object 표현식으로 만들 수 있다.

object 표현식 사용해보기1번을 통해 익명 객체가 object 표현식으로 만들어졌다. 이 익명 객체는 Superman 클래스를 상속해 fly() 메서드를 오버라이딩 하고 있다.
즉, 하위 클래스를 만들지 않고도 Superman 클래스의 fly() 메서드를 오버라이딩 해 변경했다.


7. 다양한 클래스와 인터페이스

7-1. 추상 클래스와 인터페이스

추상 클래스

  • 선언 등의 대략적인 설계 명세와 공통의 기능을 구현한 클래스
  • 추상 클래스를 상속하는 하위 클래스에서 추상 클래스의 내용을 더 구체화 해아한다.
  • abstract라는 키워드와 함께 선언

인터페이스

  • 추상과 비슷하지만 프로퍼티에 상태 정보를 저장할 수 없다.
  • 다중 상속이 가능하다.

추상 클래스의 정의와 구현

추상 클래스 Vehicle 사용해 보기추상 클래스에서는 상속을 위해 open 키워드를 사용할 필요가 없다. 마찬가지로 추상 프로퍼티나 메서드에도 open이 필요없다. 하지만 일반 프로퍼티나 메서드를 오버라이딩하려면 open 키워드가 필요하다.

만약 추상 클래스로부터 하위 클래스를 생성하지 않고 단일 인스턴스로 객체를 생성하려면 object를 사용하여 지정할 수 있다.

추상 크래스의 객체 인스턴스

인터페이스

  • abstract로 정의된 추상 메서드나 일반 메서드가 포함된다.
  • 다른 객체 지향 언어와 다르게 메서드에 구현 내용이 포함될 수 있다.
  • 프로퍼티를 통해 상태를 저장할 수 없다. 선언만 가능. val의 get()제외
  • 인터페이스 또한 객체를 생성할 수 없고 하위 클래스를 통해 구현 및 생성해야 한다.

인터페이스의 선언과 구현

  • 인터페이스는 interface 키워드를 사용해 선언하고 상속한 하위 클래스에서는 override를 사용해 해당 메서드를 구현해 주어야 한다.
interface 인터페이스 이름 [: 인터페이스 이름...] {
	추상 프로퍼티 선언
    추상 메서드 선언
    [일반 메서드 선언 {...}]
}

메서드는 추상, 일반 모두 가능하지만 프로퍼티는 오직 추상 메서드로만 선언해야 한다.

Pet 인터페이스 만들어 보기
코틀린에서는 상속, 구현 모두 콜론(:)을 통해 정의

게터를 구현한 프로퍼티
인터페이스에서는 프로퍼티에 값을 저장할 수없지만, val로 선언된 프로퍼티는 게터를 통해 필요한 내용을 구현할 수 있다.

여러 인터페이스를 이용한 다중 상속인터페이스에서 동작의 이름이 같은 경우 super<인터페이스 이름>.메서드 이름()형태로 구분할 수 있다.

인터페이스 위임

  • by 위임자를 사용할 수 있다.

interface Nameable {
    var name: String
}

class StaffName : Nameable {
    override var name: String = "Sean"
}

class Work : Runnable { // 스레드 실행을 위한 인터페이스 
    override fun run( ){
        println("work...")
    }
}

// 1. 각 매개변수에 해당 인터페이스를 위임
class Person(name: Nameable, work: Runnable): Nameable by name, Runnable by work

fun main() {
    val person = Person(StaffName(), Work()) // 2. 생성자를 사용해 객체 바로 전달
    println(person.name) // 3. 여기서 StaffName 클래스의 name 접근
    person.run() // 4. 여기서 Work 클래스의 run 접근
}

커피 제조기 만들어 보기

히터 Heater 인터페이스

전기 히터 ElectricHeater 클래스

펌프 Pump 클래스

열사이펀 Thermosiphon 클래스

커피 모듈 CoffeModule 인터페이스

드립 커피 모듈 MyDripCoffeeModule 클래스

커피 제조기 CoffeeMaker 클래스와 생성 테스트하기


7-2 데이터 클래스와 기타 클래스

데이터 전달을 위한 데이터 클래스
DTO(Data Transfer Object) - 구현 로직을 가지고 있지 않고 순수한 데이터 객체를 표현한다. 또한 게터세터, toString(), equals()등과같은 메서드를 가져야 하지만 코틀린은 자동으로 생성해준다.

데이터 클래스 조건

  • 주 생성자는 최소한 하나의 매개변수를 가져야 한다.
  • 주 생성자의 모든 매개변수는 val, var로 지정된 프로퍼티여야 한다.
  • 데이터 클래스는 abstract, open, sealed, inner 키워드를 사용할 수 없다.
  • 부 생성자나 init 블록을 넣어 데이터를 위한 간단한 로직을 포함 할 수 있다.

객체 디스트럭처링 하기

  • 객체가 가지고 있는 프로퍼티를 개별 변수로 분해하여 할당하는 것.
  • 변수를 선언할 때 소괄호를 사용해서 분해하고자 하는 객체를 지정한다.
val (name, email) = cus1
val (_, email) = cu1 // 첫 번째 프로퍼티 제외
println("$name, $email")

val name2 = cus1.component1()
val email2 = cus1.component2()

내부 클래스 기법
1. 중첩 클래스 - 클래스 안에 또 다른 클래스가 정의되어 잇는 것.
2. 이너 클래스

// 정적 클래스처럼 사용한 코틀린의 중첩 클래스
class A {
	class B { // 코틀린에서는 아무 키워드가 없는 클래스는 중첩 클래스이며 정적 클래스처럼 사용
    	... // 외부 클래스 A의 프로퍼티, 메서드에 접근할 수 없음.
    }
}

// 코틀린의 이너 클래스
class A {
	inner class B { // 자바와 달리 inner 키워드 필요
    	... // 외부 클래스 A의 필드에 접근 가능
    }
}

중첩 클래스

  • 중첩 클래스는 기본적으로 정적(static) 클래스처럼 다루어진다. 즉, 중첩 클래스는 객체 생성 없이 접근 할 수 있다.

중첩 클래스 사용하기 일반적으로 중첩 클래스는 바깥 클래스에 접근 할 수 없지만 Outer 클래스가 컴패니언 객체(static처럼 접근 가능)를 가지고 있을 때는 접근이 가능하다.

이너 클래스

  • 클래스 안에 이너 클래스를 정의할 때 이너 클래스는 바깥 클래스의 멤버들에 접근할 수 있다. 심지어 private 멤버도 접근이 가능하다.
profile
배운거 정리하기

0개의 댓글