/**
* 두개의 인터페이스를 상속받은 Button class
*/
class Button : Clickable, Focusable {
override fun click() = println("I was clicked") //override 키워드
//이름이 같은 함수가 있는 두 interface를 상속한 경우라면 해당 함수를 override 구현해야함
override fun showOff() {
//두 인터페이스중 하나의 함수만 필요한 경우 하나의 함수만 호출해도 됨
//상위 호출 : super<interface>.함수()
super<Clickable>.showOff()
super<Focusable>.showOff()
}
}
/**
* 일반메서드와 디폴트메서드가 있는 Clickable interface
*/
interface Clickable {
fun click() //일반 메서드 선언
fun showOff() = println("I'm clickable!")
//디폴트 구현이 있는 메서드 -> 오버라이딩 해도 되고 안할 경우 여기서 구현된 함수 사용
}
/**
* Clickable과 동일한 함수인 showOff()가 존재하는 Focusable
*/
interface Focusable {
fun setFocus(b: Boolean) =
println("I ${if (b) "got" else "lost"} focus.")
fun showOff() = println("I'm focusable!")
}
Effective Java 구절 : 상속을 위한 설계와 문서를 갖추거나, 그럴 수 없다면 상속을 금지하라
/**
* 구현 인터페이스
*/
interface Clickable {
fun click() //해당 함수는 반드시 구현해야함, 왜냐? 구현을 안했으니 자식이 해줘야쥬?
fun showOff() = println("I'm clickable!") //디폴트 함수, 구현 해도 되고 안해도 됨
}
/**
* Clickable 을 상속받는 상속이 가능한(open) RichButton 클래스
*/
open class RichButton : Clickable { //open class -> 다른 클래스가 상속 가능
fun disable() {} //기본은 final -> 하위 클래스가 해당 메서드를 오버라이드 할 수 없다.
open fun animate() {} //open -> 하위 클래스에서 해당 메서드를 오버라이드 할 수 있다.
override fun click() {}
//override -> 오버라이드한 메서드는 기본적으로 열려있다. -> 하위 클래스에서 구현가능
//만일 final override인 경우 -> RichButton 을 상속받는 클래스에서 override 불가능
//=> 나는 지금 오버라이드(재정의)하지만 나(RichButton)를 상속받는 하위클래스에선 click()을 재정의할 수 없어!! 라는 의미
}
/**
* interface View의 구현 클래스 Button
* interface는 생성자가 없어서 ()가 X -> View
* 클래스 상속시 생성자 존재해서 ()가 붙음
*/
class Button : View {
override fun getCurrentState(): State = ButtonState()
override fun restoreState(state: State) { /*...*/ }
class ButtonState : State { /*...*/ }
//앞에 inner가 X -> 코틀린에서는 중첩클래스가 기본
}
/**
* 바깥쪽 클래스 Outer
*/
class Outer {
//코틀린에서는 class 앞에 inner를 붙이면 내부클래스가 된다
inner class Inner {
// 내부 클래스에서 외부클래스 참조에 접근하려면 this@외부클래스 라고 써야함
fun getOuterReference(): Outer = this@Outer
}
}
sealed
를 붙이면 그 상위 클래스를 상속한 하위 클래스 정의 제한 가능/**
* 봉인된 sealed 클래스, 아래 하위 클래스에 대해서만 when에 지정 가능(else X)
* 앞에 sealed를 붙인다.
*/
sealed class Expr {
class Num(val value: Int) : Expr()
class Sum(val left: Expr, val right: Expr) : Expr()
}
fun eval(e: Expr): Int =
when (e) {
is Expr.Num -> e.value
is Expr.Sum -> eval(e.right) + eval(e.left)
//else X 🌝
}
class User (val n:String)
-> 제일 간결(추천!)class User constructor (_n:String) { val n:String init {n=_n}
class User (val n:String){val n = _n}
class User (val n:String, val d:Boolean=true)
class RadioButton:Button()
class RadioButton:Button
-> 괄호Xclass Secretive private constructor(){}
-> 인스턴스화 Xinterface User {
val nickname: String //하위 클래스가 nickname을 얻을 수 있는 방법을 구현해야함
}
class PrivateUser(override val nickname: String) : User //()가 X -> interface 이기 때문
//아래 두 클래스의 구현이 다름을 살펴볼 것
class SubscribingUser(val email: String) : User {
override val nickname: String //호출시마다 새로 계산
get() = email.substringBefore('@') //커스텀 게터
}
class FacebookUser(val accountId: Int) : User {
//프로퍼티 초기화 식
//객체 초기화시 계산 데이터를 뒷받침필드에 set & get 하는 형식 -> 객체 생성시 한번만 계산
override val nickname = getFacebookName(accountId)
}
class User(val name: String) {
// address 세터에서 뒷받침하는 필드 접근
var address: String = "unspecified"
/**
* field 가 뒷받침필드임 -> 필드에 접근 가능
* getter : 읽기 가능
* setter : 읽기 + 쓰기 모두 제공
*/
set(value: String) {
println("""
Address was changed for $name:
"$field" -> "$value".""".trimIndent())
field = value
}
}
val user = User("Bona")
user.address = "Elsenheimerstrasse 47, 80687 Muenchen"
//세터 호출
>> Address was changed for Bona:
"unspecified" -> "Elsenheimerstrasse 47, 80687 Muenchen".
field
사용시 "unspecified" 로 출력//기본클래스 Client
class Client(val name: String, val postalCode: Int)
toString()
: 객체 문자열 표현 오버라이드 해야 Client@5e9f2dsf
같은 알아보기 힘든 문자열이 안나옴==
+ toHashCode()
equals
(==) 오버라이드시 반드시 toHashCode()
를 오버라이드 해야 함 : equals()가 true를 반환하는 두 객체는 반드시 같은 hashCode()를 반환해야 하기때문 -> 해시 코드가 같아야만 equals에서 실제 값을 비교위 세가지 함수를 오버라이드 하지 않으려면?
//data class Client -> 위 세 함수 오버라이드 필요 X
data class Client(val name: String, val postalCode: Int)
by
키워드를 통해 위임중임을 명시 가능/**
* 필요한 함수만 새로 구현(override)하는 위임된 클래스 TestSet
*/
class TestSet<T>(
val innerSet: MutableCollection<T> = HashSet<T>()
) : MutableCollection<T> by innerSet { //MutableCollection의 구현을 innerSet에게 위임
var objectsAdded = 0
//아래 메서드는 위임하지 않고 새로 구현한다.
override fun add(element: T): Boolean {
objectsAdded++
return innerSet.add(element)
}
//오버라이드 하지 않은 그밖에 MutableCollection 함수는 그냥 사용 가능!
}
=> 객체 생성시 사용한다는 공통점 존재
Payroll.allEmployees.add(Person())
: object.프로퍼티(arrayList).프로퍼티함수fun getFacebookName(accountId: Int) = "fb:$accountId"
//User 클래스의 주생성자가 private or protected 가 아니라면 아래와 같이 확장함수로 새로운 팩토리 메서드 선언 가능
//why? 확장함수는 해당 클래스의 protected, private 멤버에 접근 불가
//fun User.Companion.newSecretUser(userId:Int) :User = User(userId.hashCode().toString())
/**
* 주 생성자를 private 으로 만든다
*/
class User private constructor(var nickname: String) {
/**
* 동반 객체로 선언 -> private 생성자 접근 가능
* 동반객체에 이름 붙이기 가능 : companion Object newUser => User.newUser.newSubscribingUser("bob@gmail.com")
* 아래는 이름이 없는 동반객체 => User.newSubscribingUser("bob@gmail.com")
*/
companion object { //:인터페이스 or :클래스()로 상속 가능
fun newSubscribingUser(email: String) =
User(email.substringBefore('@')) //@이전것만 set
fun newFacebookUser(accountId: Int) =
User(getFacebookName(accountId)) //페이스북 사용자 ID로 사용자 생성하는 팩토리 메서드
}
}
val subscribingUser = User.newSubscribingUser("bob@gmail.com")
println(subscribingUser.nickname) //bob