일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- Abstract Factory
- ㅓ
- 추상팩토리패턴
- 프로토타입 패턴
- 빌터패턴
- F
- builderPattern
- 디자인패턴 #
- 디자인패턴
- Kotlin
- Observer Pattern
- 코틀린
- El
- 추상 팩토리
- designPattern
- Singleton
- factory method
- 함수형프로그래밍
- PrototypePattern
- r
- 팩토리 메소드
- Design Pattern
- ㅋㅁ
- Functional Programming
- 싱글톤
- a
- 옵저버 패턴
- Today
- Total
오늘도 더 나은 코드를 작성하였습니까?
클래스 객체 인터페이스(생성자와 프로퍼티를 갖는 클래스 선언) 본문
코틀린은 주(primary) 생성자와 부(secondary) 생성자를 구분한다.
초기화 블록을 통해 초기화 로직을 추가할 수 있다.
클래스 초기화: 주 생성자와 초기화 블록
class User( 주 생성자 ){ ... }
주 생성자는 파라미터를 지정하고 그 생성자 파라미터에 의해 초기화되는 프로퍼티를 정의하는 2가지 목적을 가지고 있다.
class User constructor(_nickname: String){
val nickname: String
init {
nickname = _nickname
}
}
* _nickname은 자바처럼 this.nickname = nickname ; 과같은 파라미터와 멤버 변수의 이름이 동일할 때 모호성을 없애기 위해 사용된다.(this를 사용해도 된다.)
contstructor - 주 생성자 또는 부 생성자를 정의를 시작할 때 사용된다.
init - 초기화 블록을 설정할 때 시작 사용된다.
초기화 블록
- 객체 생성될 때 호출된다.
- 주 생성자는 제한적이라 필요한 코드가 있으면 초기화 블록으로 처리한다.
- 여러 개 선언할 수 있다.
class User (_nickname: String){
val nickname: String = _nickname
}
별다른 로직이 필요 없어 초기화 블록을 없애버렸다.
contstructor 주 생성자 앞에 별다른 애노테이션이나 가시성 변경자를 넣을 필요가 없다면 생략 가능하다.
*프로퍼티를 초기화하는 식이나 초기화 블록 안에서만 주 생성자의 파라미터를 참조할 수 있다.
class User (val nickname: String)
별다른 로직이 필요 없고 주 생성자 파라미터로 바로 프로퍼티를 초기화한다면 val 키워드를 붙여 정의와 초기화를 간략하게 할 수 있다.
class User (val nickname: String, val isSubscribed: Boolean = true)
함수 파라미터와 동일하게 주 생성자 파라미터 역시 default 값을 지정할 수 있다.
*만약 모든 주 생성자 파라미터에 default 값을 지정한다면 코틀린 컴파일러는 자동으로 파라미터가 존재하지 않는(디폴트 값으로만 구성되도록) 생성자를 만들어준다. DI(의존성 주입) 라이브러리를 이용할 때 유용하다.
*자바와 같이 어떤 생성자도 생성하지 않는다면 빈 생성자를 생성해준다.
--> 상속 및 인터페이스 구현 시 : className() 괄호가 존재하면 클래스다.
open class User (val nickname: String){}
class TwitterUser(nickname: String): User(nickname){ }
오픈된 클래스인 User를 상속하고 상속받을 User의 nickname 프로퍼티에 주생 성자의 파라미터인 nickname의 값을 넘겨준다.
class TwitterUser private constructor(nickname: String): User(nickname){ }
주 생성자를 통해 객체 생성을 막으려면 private 변경자를 붙이면 된다.
부 생성자: 상위 클래스를 다른 방식으로 초기화
* 인자에 디폴트 값을 제공하기 위해 부 생성자를 여러 개 만들면 안된다. 대신 파라미터의 디폴트 값을 제공해라
이 클래스는 주 생성자를 선언하지 않고 2개의 부 생성자로 이루어져 있다.
부 생성자는 constructor 키워드로 시작하고 파라미터를 받을 수 있고 블록 안에 로직을 넣을 수 있다.
open class View{
constructor(ctx: Context){
}
constructor(ctx: Context, attr: AttributeSet){
}
}
: super(parm) { ... } 자바와 같이 super키워드로 생성을 상위 클래스에 위임할 수 있다.
:this(parm)으로 동일 클래스 다른 생성자에게 위임한다.
class MyButton: View{
constructor(ctx: Context): this(ctx, My_STYLE){}
constructor(ctx: Context, attr: AttributeSet): super(ctx, attr){}
}
* 클래스에 주 생성자가 존재하지 않는다면
모든 부 생성자는 반드시 상위 클래스를 초기화하거나 다른 생성자에게 생성을 위임해야 된다.
* 부생성자의 주된 목적은 자바와의 상호운용에 있다.
인터페이스에 선언된 프로퍼티의 구현
interface User{
val nickname: String
}
class PrivateUser(override val nickname: String): User
class SubscribingUser(val email: String): User{
override val nickname: String
get() = email.substringBefore("@")
}
class FacebookUser(val accountId: Int): User{
override val nickname = getFacebookName(accountId)
private fun getFacebookName(accountId: Int): String = "hello"
}
fun main() {
val user1 = PrivateUser(nickname = "hoho")
val user2 = SubscribingUser(email = "hahaha@gmail.com")
val user3 = FacebookUser(accountId = 1)
println(user1.nickname)
println(user2.nickname)
println(user3.nickname)
}
PrivateUser
- 주 생성자 안에 프로퍼티에 직접 선언 후 override를 통해 추상 프로퍼티를 구현한다.
SubscribingUser
- 커스텀 겟터를 통해 추상 프로퍼티를 설정한다 백킹 필드에 값을 저장하는 것이 아님.
FacebookUser
- 초기화 함수로 추상 프로퍼티를 초기화한다.
*SubscribingUser nickname은 매번 호출될 때마다 계산되는 겟터를 사용하고
FacebookUser 한번 식을 이용한 저장으로 필드에 저장 후 불러온다.
interface User{
val email: String
val nickname: String
get() = email.substringBefore("@")
}
인터페이스는 추상 프로퍼티뿐 아니라 겟터 셋터를 가있는 프로퍼티를 선언할 수 있는데 이때 백킹 필드는 없다
그 이유는 인터페이스는 상태를 저장할 수 없다.
하위 구현 클래스는 email 은 반드시 override로 구현을 해야 되지만 nickname 같은 경우는 오버라이드 하지 않고 상속할 수 있다.
게터와 세터에서 백킹 필드에 대한 접근
값을 저장하는 동시에 로직을 실행할 수 있게 하기 위해서는 접근자(get set) 안에서 프로퍼티의 백킹 필드에 접근할 수 있어야 한다.
class User{
var address: String = "unspecificed"
set(value: String){
println("""
Address was Changed :
"$field" -> $value.
""".trimIndent())
field = value
}
}
fun main() {
val user = User()
println(user.address)
user.address= "우리집이야!"
println(user.address)
}
field라는 식별자를 이용해서 백킹 필드에 접근할 수 있으며 getter에서는 읽을 수만 있고 setter에서는 쓸 수만 있다.
컴파일러는 디폴트 겟터 셋터를 사용하건 커스텀 접근자 정의 하던 field를 사용하는 프로퍼티에 백킹 필드를 생성한다.
하지만 field를 사용하지 않는 커스텀 접근자를 구현을 정의했다면 백킹 필드는 존재하지 않는다.
(val 은 get var 은 get set에서 모두 사용하지 않는다면)
접근자(getter, setter)의 가시성 변경
접근자의 가시성은 기본적으로 프로퍼티 가시성과 동일하지만 추가로 가시성 변경자를 통해 변경할 수 있다.
class LengthCounter{
var counter: Int = 0
private set
fun addWord(word: String) {
counter += word.length
}
}
counter 프로퍼티는 추가되는 단어의 길이만큼 증가해야 돼서 변경 가능한 멤버 변수이다.
하지만 외부에서 추가적인 변경을 하면 안 되기 때문에 set을 private로 지정하였다.
프로퍼티에 추가적으로 다룰내용
lateinit는
null 이 될 수 없는 프로퍼티에 지정하면 프로퍼티를 생성자가 호출된 다음에 초기화하겠단 것이다.
lazy initalized는
요청이 들어오면 비로소 초기화된다, 더 일반적인 위임 프로퍼티이다.
@JvmField
프로퍼티에 붙이면 접근자가 없는 public 필드를 만든다.
const
애노테이션을 더 편리하게 다룰 수 있고 원시 타입 및 String 타입인 값을 애노테이션의 인자로 활용할 수 있다.
'Kotlin in Action > 코틀린 기초' 카테고리의 다른 글
클래스 객체 인터페이스(Object 키워드 : 클래스 선언과 인스턴스 생성) (0) | 2020.08.14 |
---|---|
클래스 객체 인터페이스(컴파일러가 생성한 메소드: 데이터 클래스와 클래스 위임) (0) | 2020.08.14 |
클래스 객체 인터페이스(클래스 계층 정의) (0) | 2020.08.12 |
함수 정의와 호출 (0) | 2020.08.08 |
코틀린 기초 (0) | 2020.08.06 |