지금까지 코틀린 기본세팅을 진행했다.
2022.01.29 - [Kotlin] 코틀린 기본개념 - 코틀린이란? 도대체 왜 쓰는가? 특징!
2022.01.28 - [Kotlin] 코틀린 환경설정 - Intellj에서 코틀린 프로젝트 생성하기
2022.01.29 - [Kotiln] Intellj에서 코틀린 코딩컨벤션 Kotlin Style Guide 적용하기
2022.01.30 - [Kotiln] 코틀린 실행하기 - Scratch File (스크래치 파일) 만들기
그리고 간단한 기본문법도 살펴봤다.
2022.01.29 - [Kotiln] 코틀린 기본문법1 (변수선언/함수선언/자료형/반복문/조건문)
2022.01.30 - [Kotiln] 코틀린 기본문법2 (NULL처리 : lateinit/lazy/!!/?/?.)
2022.01.30 - [Kotiln] 코틀린 기본문법3 (클래스와 컬렉션: List/Map/Set)
약간 심화문법의 기준을 어디까지 나눠야할지 감이 안잡혀서 실제 코드를 보면서 낯선 개념 위주로 정리해볼 생각이다
2022.01.31 - [Kotiln] 코틀린 심화문법1 (람다식:lambda expression)
2022.01.31 - [Kotiln] 코틀린 심화문법2 (확장함수/고차함수/표준라이브러리)
2022.02.03 - [Kotiln] 코틀린 심화문법3 data class(데이터클래스)란?
이번엔 리팩토링하는 코드를 살펴보다가 모르는 키워드가 나와서 알아보려고 한다.
object
흔히 자바에서 사용하는 무명 내부 클래스 (anonymous inner class)
즉, 클래스를 정의하면서 객체를 생성하는 키워드다.
여기에서도 설명했지만, 다시 리마인드 하자면
코틀린에서는 클래스를 정의하자마자 생성자와 속성을 정의할 수 있다.
자바에서 생성자 정의하면서 클래스 생성하는 방식
class WhoAmI{
private val name:String
constructor(name:String){
this.name = name
}
fun myNameIs() = "나의 이름은 ${name}입니다."
}
코틀린에서 생성자 정의와 동시에 클래스 생성하는 방식
자바와 다르게 바로 newe 키워드 없이 바로 객체를 생성해서 쓸 수 있다.
class WhoAmI(private val name:String){
fun myNameIs() = "나의 이름은 ${name}입니다."
}
fun main(args: Array<String>){
val m1 = WhoAmI("영수")
val m2 = WhoAmI("미라")
println(m1.myNameIs()) //나의 이름은 영수입니다.
println(m2.myNameIs()) //나의 이름은 미라입니다.
}
코틀린에서는 object키워드를 사용해서 별다른 정의없이 싱글톤(singleton) 구현을 지원해준다.
자바에서 클래스 내부에 static 객체로 한번만 할당해주던 코드를
코틀린에서는 object키워드를 쓴 클래스로 구현해서 static객체에 할당해주는것처럼 자동으로 생성해준다.
// UserRepo 클래스 싱글톤 구현
object UserRepo {
val allUser = arrayListOf<User>()
fun getUser() {
// ...
}
}
싱글톤(singleton) 특징
싱글톤은 인스턴스가 하나만 있는 클래스를 의미한다.
위에 예시처럼 object로 싱글톤으로 구현했기 때문에, 가지는 몇가지 특징이 있다.
- UserRepo 객체를 생성하지 않고, 바로 함수와 변수를 쓸수있다.
- 생성자 자체를 사용할 수 었다. 주 생성자/ 부생성자 모두 사용할 수 없다.
- object로 생성되는 싱글톤 객체를 사용하려고 하면 INSTANCE라는 키워드를 사용해서 접근해야한다.
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
// 특징 - 객체생성 없이 바로 사용가능
UserRepo.getUser()
UserRepo.allUser.add(User("홍길동",Job("IT",Address("Seoul"))))
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
// 특징 - 생성자불가
// ERROR, object는 생성자 사용이 불가능
object UserRepo(val name:String) {
//...
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
// 특징 - INSTANCE로 object객체사용
// Kotlin의 object객체를 JAVA에서 사용하기
UserRepo.INSTANCE.getUser();
java : static
자바에는 static을 사용한다.
즉, static 키워드를 통해 클래스 멤버라고 선언하는 것이다.
그래서 static이 붙은 변수와 메소드를 각각 클래스 변수, 클래스 메소드라고 부른다.
하지만 static이 없으면 각각 인스턴스 변수, 인스턴스 메소드라고 부른다.
static이 붙은 멤버는 클래스가 메모리에 적재될때 자동으로 함께 생성되기 때문에 인스턴스 생성없디 바로 클래스명.으로 참조할 수 있다.
public final class MyClass{
static public final String TEST = "test"; //클래스 변수
static public method(int i):int{ //클래스 메소드
return i + 10
}
}
System.out.println(MyClass.TEST); //test
System.out.println(MyClass.method(1)); //11
kotin : companion object
코틀린에는 기본적으로 static이 없다.
대신 companion object를 통해 package수준의 최상위 함수와 객체선언을 사용할 수 있다.
- 최상위 함수 : Static 메소드를 대신할 수 있는 함수
- 객체선언 : 최상위 함수가 접근할 수 없는 클래스 안에 있는 private 멤버변수에 접근할때 사용하는 객체
아래의 예시에서 보면 constructor를 접근제한자를 private 클래스로 선언했다.
이럴 경우 해당 클래스는 외부에서는 접근제한자 때문에 직접 호출이 불가능하다.
하지만 해당 클래스 내부에 bar함수는 companion object로 선언했다.
그래서 이 bar함수는 private클래스 내부에 있지만, 외부에서 접근이 가능하다.
class A private constructor(val name: String) { // 클래스 A의 주 생성자는 private로 접근 제한
companion object {
fun bar() : A {
return A("zero") // companion object의 bar() 메서드를 통해 private 생성자 접근 가능
}
}
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
// ERROR, 주 생성자는 private 접근제한자로 설정되어 호출 불가능
var a = A("TEST")
// SUCCESS, 동반객체의 bar() 메서드를 통해 private 주생성자에 접근이 가능
var b = A.bar()
또한 companion object는 별도의 이름을 붙일수도 있다. 무조건 이름을 사용해야할 필요는 없고, 생략할수도 있다.
class A private constructor(val name: String) { // 클래스 A의 주 생성자는 private로 접근 제한
companion object B {
fun bar() : A {
return A("zero") // companion object의 bar() 메서드를 통해 private 생성자 접근 가능
}
}
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
// 사용 방법
A.bar() // 동반객체 이름 생략
A.B.bar() // 동반객체 이름 사용
B.bar() // 클래스명 생략, 동반객체 이름만 사용
하지만, 클래스 내에는 companion object 를 오직 딱 하나만 선언할 수 있다.
별도의 이름을 부여한다고 해도 같다. 덕분에 static변수들을 한곳에 모이게 할 수 있다.
class MyClass5{
companion object MyCompanion1{
val prop1 = "나는 Companion object의 속성이다."
fun method1() = "나는 Companion object의 메소드다."
}
companion object MyCompanion2{ // -- 에러발생!! Only one companion object is allowed per class
val prop2 = "나는 Companion object의 속성이다."
fun method2() = "나는 Companion object의 메소드다."
}
}
companion object는 static클래스변수가 아니라 object 객체이다.
위의 예시를 보면, compain object는 단순히 java의 static 을 대체한 클래스 변수처럼 보인다.
하지만 실제는 static하게 동작하기만 할 뿐이고, 사실은 객체이다.
class MyClass2{
companion object{
val prop = "나는 Companion object의 속성이다."
fun method() = "나는 Companion object의 메소드다."
}
}
fun main(args: Array<String>) {
//사실은 MyClass2.맴버는 MyClass2.Companion.맴버의 축약표현이다.
println(MyClass2.Companion.prop) //Myclass2.prop와 같은표현
println(MyClass2.Companion.method()) //Myclass2.method()와 같은표현
}
그래서 이렇게 클래스명.Companion.변수 이 형태로 쓸 수도 있다.
즉 companion obejct는 클래스가 메모리에 적재되면서 함께 생성되는 동반객체인 것이다. 그래서 클래스명.Companion.변수 으로 접근할 수 있는 것이고, 이에 대한 축약표현이 단지 클래스명.변수이기 때문에 단순히 static 클래스변수같은 착각이 드는 것이다.
결국,companion object는 static클래스변수가 아니라 object 객체이기 때문에 아래와 같이 쓸수도 있다
class MyClass2{
companion object{
val prop = "나는 Companion object의 속성이다."
fun method() = "나는 Companion object의 메소드다."
}
}
fun main(args: Array<String>) {
println(MyClass2.Companion.prop)
println(MyClass2.Companion.method())
val comp1 = MyClass2.Companion //companion object객체라서 변수할당가능
println(comp1.prop) //멤버접근
println(comp1.method())
val comp2 = MyClass2 //Companion 생략하고, companion object객체생성
println(comp2.prop) //멤버접근
println(comp2.method())
}
그래서 클래스명.Companion으로 객체를 생성할수도 있고, Companion을 생략하고 객체를 생성할 수도 있다.
이렇게 생성된 객체에 .멤버변수를 통해 접근할 수 있다.
참고
https://www.bsidesoft.com/8187
https://jaejong.tistory.com/105
'🍃 Language > Kotiln' 카테고리의 다른 글
[Kotlin] 굉장히 초라한 API 설계..와 구조? (+ JSON 생성기) (0) | 2023.06.18 |
---|---|
[Kotlin] 아규먼트(Argument) 자동완성 플러그인 :: Fill Class (0) | 2022.08.14 |
[Kotlin] 코틀린 오류 온통 빨갛게 물들어버렸다 - Invalidate Caches (0) | 2022.02.05 |
[Kotlin] 코틀린 심화문법3 (data class와 open class) (0) | 2022.02.03 |
[Kotlin] 코틀린 코루틴 개념과 basics - 새차원 강의1,2요약 (0) | 2022.01.31 |
[Kotlin] 코틀린 심화문법2 (확장함수/고차함수/표준라이브러리) (0) | 2022.01.31 |