일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- F
- 옵저버 패턴
- Singleton
- 추상팩토리패턴
- 추상 팩토리
- a
- 프로토타입 패턴
- 싱글톤
- 함수형프로그래밍
- El
- builderPattern
- 디자인패턴
- Design Pattern
- PrototypePattern
- ㅓ
- Abstract Factory
- designPattern
- ㅋㅁ
- r
- 디자인패턴 #
- 코틀린
- Kotlin
- factory method
- 빌터패턴
- Observer Pattern
- Functional Programming
- 팩토리 메소드
- Today
- Total
오늘도 더 나은 코드를 작성하였습니까?
클래스 객체 인터페이스(Object 키워드 : 클래스 선언과 인스턴스 생성) 본문
Object 키워드 : 클래스 선언과 인스턴스 생성
코틀린에서 object 키워드는 다양한 상황에서 사용하지만 모든 경우 클래스를 정의하면서 동시에 객체를 생성한다는 공통점이 있다.
1. 객체 선언은 싱글턴을 정의하는 방법 중 하나다.
2. 동반 객체(companion object)는 인스턴스 메서드는 아니지만 어떤 클래스와 관련 있는 메서드와 팩토리 메서드를 담을 때 쓰인다. 동반 객체 메서드에 접근할 때는 동반 객체가 포함된 클래스의 이름을 사용할 수 있다.
3. 무명 내부 클래스(익명 객체) 대신 쓰인다
객체 선언
- 클래스 선언과 그 클래스의 단일 인스턴스의 선언을 합친 것이다.
- 클래스를 정의하고 인턴스를 만들어 변수에 저장하는 모든 작업을 단 한 문장 obeject {... }로 줄인 것이다.
- 프로퍼티, 메서드, 초기화 블록 등이 등이 들어갈 수 있다.
- 주 생성자, 부 생성자는 사용할 수 없다.
- 객체 선언문이 있는 위치에서 생성자 호출 없이 즉시 만들어지기 때문에 생성자 정의가 필요 없다.
data class Person(val name: String, val salary: Int)
object Payroll{
val allEmployees = arrayListOf<Person>()
fun calculateSalary(){
for (person in allEmployees){
println(person.toString())
}
}
}
fun main() {
Payroll.allEmployees.add(Person("hik",100))
Payroll.allEmployees.add(Person("huk",300))
Payroll.calculateSalary()
}
- 클래스나 인터페이스를 상속할 수 있다.
예시) Comparator는 두 객체를 받아 그중 어느 것이 큰지 알려주는 정수를 반환한다.
보통 클래스마다 1개씩 필요하기 때문에 객체 선언으로 만들면 좋다.
object CaseInsensitiveFileComparator: Comparator<File>{
override fun compare(file1: File, file2: File): Int {
return file1.path.compareTo(file2.path, ignoreCase = true)
}
}
* o1 > o2 return 1
o1 = o2 return 0
o1 < o2 return -1
- 클래스 안에서 객체를 선언 가능하다. 단, 인스턴스는 단 하나뿐이다.
data class Person(val name: String){
object NameComparator: Comparator<Person>{
override fun compare(o1: Person, o2: Person): Int = o1.name.compareTo(o2.name, ignoreCase = true)
}
}
fun main() {
val persons = listOf(Person("alice"), Person("Bob"), Person("star"), Person("hik"))
println(persons.sortedWith(Person.NameComparator))
}
* 코틀린의 객체 선언은 유일한 인스턴스에 대한 정적인 필드가 있는 자바 클래스로 컴파일된다. 이때 인스턴스의 필드 이름은 INSTANCE이다.
ClassName.INSTANCE.methodName(arg...)로 호출한다. 자바에선.
동반 객체: 팩토리 메서드와 정적 멤버가 들어갈 장소
코틀린 클래스에는 정적 멤버가 없다 static 키워드 지원하지 않는다.
대부분은 최상위 함수와 객체 선언으로 대체하고 최상위 함수 선언을 더 권장한다.
하지만 최상위 함수는 비공개 멤버에 접근할 수 없기 때문에 클래스 내부 정보에 접근할 필요가 있을 때는 객체 선언을 사용한다.
- 클래스 안에 정의된 객체 선언에 companion 특별한 표시를 붙이면 그 클래스의 동반 객체가 된다.
- 동반 객체의 프로퍼티나 메서드에 접근하려면 그 동반 객체가 정의된 클래스의 이름을 사용한다.
- 동반 객체의 이름은 따로 정의할 수 있지만 안 해도 둘러싼 클래스의 이름으로 호출 가능하다.
- 클래스 내에 2개 이상 companion object를 쓰는 것은 안 된다
private 생성자를 호출하기 좋은 위치
동반 객체는 자신을 포함하는 클래스의 private생성자 호출 가능하다. 따라서 동반 객체는 팩토리 패턴을 구현하기 가장 적합한 위치이다.
예시)
class User {
val nickName: String
constructor(email:String){
nickName = email.substringBefore("@")
}
constructor(facebookAccountId: Int){
nickName = getFacebookName(facebookAccountId)
}
private fun getFacebookName(facebookAccountId: Int): String {
return "test"
}
}
class User private constructor(val nickname: String){
companion object{
fun newSubscribingUser(email: String)
= User(nickname = email.substringBefore("@"))
fun newFacebookUser(accountId: Int)
= User(getFacebookName(accountId))
private fun getFacebookName(accountId: Int): String = "facebook user"
}
}
fun main() {
val subscribingUser = User.newSubscribingUser("hoho@gmail.com")
val facebookUser = User.newFacebookUser(14)
println(subscribingUser.nickname)
println(facebookUser.nickname)
}
* 클래스를 확장해야 되는 경우 동반 객체는 하위 클래스에서 오버라이드 할 수 없기 때문에 여러 개의 생성자를 두는 것이 나은 해법이다.
동반 객체를 일반 객체처럼 사용하기
- 동반 객체 역시 클래 안에 정의된 일반 객체다.
- 이름 붙이기, 상속, 확장 함수, 프로퍼티를 정의할 수 있다.
- 이름을 따로 안 붙이면 Companion이 된다.
class Person(val name: String){
companion object Loader{
fun fromJSON(jsonText: String) = Person(jsonText.substringAfter("'").substringBefore("'"))
}
}
fun main() {
val person1 = Person.fromJSON("{name: 'Dmitry'}")
val person2 = Person.Loader.fromJSON("{name: 'hik'}")
println(person1.name)
println(person2.name)
}
동반 객체 인터페이스 구현하기
interface JSONFactory<T>{
fun fromJSON(jsonText: String): T
}
class Person(val name: String){
companion object: JSONFactory<Person>{
override fun fromJSON(jsonText: String): Person
= Person(jsonText.substringAfter("'").substringBefore("'"))
}
}
fun <T> loadFromJSON(factory: JSONFactory<T>): T{
// json 으로부터 각원소를 다시만들어내는 추상팩토리가 가 있다면
}
fun main() {
// Person class 이름을 사용했다.
loadFromJSON(Person)
}
*자바에서 사용할 때 코틀린 멤버를 정적 멤버로 만들 필요가 있다면 @JvmStatic
정적 필드가 필요하다면 @jvmField를 최상위 프로퍼티나 클래스에서 선언된 프로퍼티 앞에 붙인다.
동반 객체의 확장
- 동반객체에 확장 함수를 정의하려면 원래 클래스에 반드시 동반 객체를 꼭 선언해야 된다.
빈 동반 객체라도 선언해야 된다.
class Person(val name: String, val lastname: String){
companion object{
}
}
fun Person.Companion.fromJSON(jsonText: String): Person{
...
return Person( ... )
}
객체 식: 무명 내부 클래스를 다른 방식으로 작성
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener(object: View.OnClickListener{
override fun onClick(p0: View?) {
TODO("Not yet implemented")
}
})
}
}
*코틀린 무명 클래스는 여러 인 터페이스를 구현하거나 클래스를 확장하면서 인터페이스를 구현할 수 있다.
* 무명객체는 싱글턴이 아니라 객체 식이 사용될 때마다 생성된다.
*객체식 안의 코드는 그식이 포함된 함수의 변수에 접근할 수 있다. 자바와 달리 final 아닌 변수도 객체 식 안에서 이용할 수이다.
fun countClicks(){
var clickCount = 0
button.setOnClickListener(object: View.OnClickListener{
override fun onClick(p0: View?) {
clickCount++
}
})
}
* 객체식은 무명 객체 내에서 여러 메서드를 오버라이드 하는 경우 훨씬 더 효과적이다.
메서드가 하나뿐인 인터페이스를 구현해야 된다면 SAM(Single Abstract Method) 변환을 활용하는 편이 낫다.
'Kotlin in Action > 코틀린 기초' 카테고리의 다른 글
람다로 프로그래밍(컬렉션 함수형 API) (0) | 2020.08.16 |
---|---|
람다로 프로그래밍 (0) | 2020.08.16 |
클래스 객체 인터페이스(컴파일러가 생성한 메소드: 데이터 클래스와 클래스 위임) (0) | 2020.08.14 |
클래스 객체 인터페이스(생성자와 프로퍼티를 갖는 클래스 선언) (0) | 2020.08.13 |
클래스 객체 인터페이스(클래스 계층 정의) (0) | 2020.08.12 |