# Object
3가지 쓰임새에 대해 알아보자
- singleton
- companion object
- anonymous object(무명객체)
# 1) Object 키워드 - 단일 인스턴스 - singleton
- object 키워드로 obect declaration(객체 선언, object 선언)을 통해 싱글턴을 간단히 사용할 수 있다
- 클래스 선언 + 클래스 인스턴스 생성(단일) 을 한번에 해결해 준다
- 클래스 안에 객체 선언도 가능 -> 그래도 단일 인스턴스
- 인터페이스,클래스 상속도 가능
- thread-safe
+ 번외 팁 : 인터페이스 구현이나, 클래스 상속 모두 ':'으로 표현하기에 구분이 헷갈릴 수 있지만..! 명칭 뒤에 '()' 가 붙으면 클래스고 없으면 인터페이스다. 아래 예제에서 Comparotor<T>는 인터페이스!
'object' 객체선언을 통한 Singleton 예제
data class Student(val name: String, val age: Int) {
//**************object declaration (객체 선언, singleton) **************
object AgeComparator : Comparator<Student> {
override fun compare(student1: Student?, student2: Student?): Int {
return student1!!.age.compareTo(student2!!.age)
}
}
}
fun main() {
val studentTina = Student("Tina", 17)
val studentJully = Student("July", 40)
val studentScott = Student("Scott", 31)
var list = listOf(studentTina, studentJully, studentScott)
println("[Before sort]$list") // Tina, July, Scott 순
list = list.sortedWith(Student.AgeComparator) //singleton 객체 넘김
println("[Before sort]$list") // Tina, Scott, July 순 (오름차순)
}
JAVA로 컴파일시
public final class Student{
...
public static final class AgeComparator implements Comparator {
@NotNull
public static final Student.AgeComparator INSTANCE;
public int compare(@Nullable Student student1, @Nullable Student student2) {
Intrinsics.checkNotNull(student1);
int var10000 = student1.getAge();
Intrinsics.checkNotNull(student2);
return Intrinsics.compare(var10000, student2.getAge());
}
// $FF: synthetic method
// $FF: bridge method
public int compare(Object var1, Object var2) {
return this.compare((Student)var1, (Student)var2);
}
private AgeComparator() {
}
static {
Student.AgeComparator var0 = new Student.AgeComparator();
INSTANCE = var0;
}
}
}
위 JAVA로 컴파일된 부분을 확인하면 싱글턴 형태로 인스턴스 명이 'INSTANCE'로 고정 돼 있는걸 확인 할 수 있다
따라서 Java에서 Kotlin 객체 선언된 싱글턴 사용할 땐
Student.AgeComparator.INSTANCE.compare(student1, student2);
# 2) Object 키워드 - Companion Object
- 어떤 클래스 내부 객체에 'companion' 변경자를 붙이면 Companion Object가 된다
- Java에서 static 메소드/필드 사용하는 것처럼 쓰면서, 클래스의 인스턴스와 무관하게, 클래스 내부 멤버 접근 필요할 경우 Companion Object를 사용한다 -> 즉 Java에서의 정적 메소드와 정적 필드를 대신한다
- static 처럼 사용한다면 그럼 그냥 최상위 함수를 사용하면 안되는가?
- -> Companion Object는 자신을 둘러싼 클래스의 private 멤버를 사용 할 수 있다 (최상위 함수는 클래스의 private 멤버에 접근 불가)
- -> 정적 메소드/필드와 같은 동작이 필요할 때, 최상위 함수를 우선으로 사용 하되, 커버가 안될 때 companion object를 사용하도록 한다
- 이름을 따로 지정 안할 시, Companion Object 이름은 'Companion' 이다 (이름 생략 가능)
- 사용하려면 {바깥클래스}.{Companion Object 이름}.{필드 or 메소드}
- Companion Object는 최상위 선언은 불가 (클래스와 동반자니까!)
- 1 Companion Object per 1 Class (클래스와 동반자니까!)
- 인터페이스,클래스 상속 가능
- 확장 함수도 사용 가능
- 자신을 둘러싼 클래스의 private 생성자를 사용 할 수 있으므로-> Factory 메소드 에 유용
Companion Object
class Student(val name: String, val age: Int) {
companion object {
fun getSchool() = "ML School"
}
}
fun main() {
println(Student.getSchool()) //static 메소드 처럼 사용한다
println(Student.Companion.getSchool()) //static 메소드 처럼 사용한다
}
Companion Object 예시2
해당 클래스에서 다루는 TAG나, Request Code에도 많이 쓴다
기존 Java에서 'public static final String' 으로 사용 되던...
class TestClass{
companion object {
private const val TAG = "TestClass"
private const val REQUEST_CAMERA = 100
}
...
}
Companion Object 예시3
싱글턴은 생성자에 파람값을 못넣는게 일반적이지만 꼭 필요한 경우 (아래 예시엔 context)
아래와 같이 companion object를 이용해 싱글턴을 이용할 수 있다
Companion Object가 팩토리 패턴 구현에 적합하다는 의미와 일맥상통 한다 (private 생성자 접근가능)
class SingleTonWithContext private constructor(){
companion object {
@Volatile
private var INSTANCE: SingleTonWithContext? = null
fun getInstance(context: Context): SingleTonWithContext =
INSTANCE ?: synchronized(this) {
INSTANCE ?: SingleTonWithContext().also {
INSTANCE = it
INSTANCE?.setContext(context)
}
}
}
private fun setContext(context: Context) {
//context set
}
}
확장함수 사용 예제
class Student(val name: String, val age: Int) {
companion object{} //확장함수 사용하려면 비어있더라도 일단 선언은 해줘야 함
}
//Companion Object 확장함수
fun Student.Companion.getSchool() ="ML Shool"
fun main() {
println(Student.getSchool())
println(Student.Companion.getSchool())
}
*'Companion Object는 자신을 둘러싼 클래스의 private 멤버를 사용할 수 있다' 고 책엔 나와있지만
실제로 테스트 해보면 private 생성자만 접근 되고, Companion Object 바깥 클래스, 그러니까 둘러싼 클래스의 private 함수나 필드에 접근이 안되는데, 번역책이라 의미가 잘못 전달 된 걸까...?
Companion Object 내부 필드 선언
class TestClass {
companion object {
private val privateVal = "privateVal"
val commonVal = "commonVal"
}
// fun testFun(){
// println(privateVal) //접근 가능
// println(commonVal) //접근 가능
// }
}
private으로 val 설정하거나
public val 설정하거나
Java로 컴파일 시, 모두 'private static final' 로 변경되는 걸 볼 수 있다
( 즉 Java에서의 정적 메소드와 정적 필드를 대신한다 )
Companion Object 내부 필드 선언 Java 변경 시
public final class TestClass {
private static final String privateVal = "privateVal";
@NotNull
private static final String commonVal = "commonVal";
@NotNull
public static final TestClass.Companion Companion = new TestClass.Companion((DefaultConstructorMarker)null);
...
public static final class Companion {
@NotNull
public final String getCommonVal() {
return TestClass.commonVal;
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
# 3) Object 키워드 - anonymouse object
- 무명 객체 정의 할 때 object 키워드를 사용 할 수 있다
- 클래스 정의 + 새 인스턴스 생성(!new!)
- '무명' 이므로 클래스 이름을 정하지 않음
anonymouse object 예시
fun anonymousTest(context: Context) {
val button: Button = Button(context)
var isClicked: Boolean //무명객체 내부에서 접근 가능
//object 무명 객체 사용 (1)
button.setOnClickListener(
object : View.OnClickListener {
override fun onClick(v: View?) {
println("onClick!!!");
isClicked = true
}
}
)
//object 무명 객체 사용 (2) : 변수에 대입 가능
val listner = object : View.OnClickListener {
override fun onClick(v: View?) {
println("onClick!!!");
isClicked = true
}
}
button.setOnClickListener(listner)
//abstract method가 1개인 경우, 무명 객체 보다
//람다식 사용이 유용하다
button.setOnClickListener {
println("onClick!!!")
isClicked = true
}
}
JAVA로 컴파일시
new OnClickListener() {
public void onClick(@Nullable View v) {
String var2 = "onClick!!!";
boolean var3 = false;
System.out.println(var2);
}
};
출처 : Kotlin In Action - 에이콘 출판사
(위 도서를 학습하고 개인 학습용으로 정리한 내용입니다)
'Kotlin' 카테고리의 다른 글
[Kotlin] 4.람다 In Collection (0) | 2021.11.16 |
---|---|
[Kotlin] 4.람다 - 기본 (0) | 2021.11.15 |
[Kotlin] 3. Data Class, by (0) | 2021.11.07 |
[Kotlin] 3.Interface, Class, 변경자, 초기화블록, 추상 프로퍼티 (0) | 2021.11.06 |
[Kotlin] 2. 코틀린 기본 - 함수(2) (0) | 2021.11.02 |