Kotlin에서는 지연 초기화(Lazy Initialization)와 관련하여 lateinit과 lazy를 제공하며, companion object를 통해 Java의 static 개념을 대체할 수 있음.
이를 Java와 비교하면서 차이점을 정리하고, lateinit vs lazy, companion object vs object 비교도 함께 제공함.
🛠️ 1. lateinit (자바의 일반적인 null 체크와 비교)
lateinit 키워드는 var로 선언된 비-Nullable 객체에 대해 나중에 초기화할 수 있도록 허용함.
✅ Java 코드 (일반적인 null 체크)
class User {
private String name;
public void setName(String name) {
this.name = name;
}
public void printName() {
if (name != null) {
System.out.println("이름: " + name);
} else {
System.out.println("이름이 초기화되지 않음!");
}
}
}
User user = new User();
user.printName(); // "이름이 초기화되지 않음!"
user.setName("홍길동");
user.printName(); // "이름: 홍길동"
🔹 특징:
• Java에서는 lateinit이 없기 때문에 null 체크를 직접 수행해야 함.
• Kotlin의 lateinit을 활용하면 null 체크 없이 지연 초기화를 간편하게 구현 가능.
✅ Kotlin 코드 (lateinit)
class User {
lateinit var name: String
fun initUser() {
name = "홍길동"
}
fun printName() {
if (::name.isInitialized) {
println("이름: $name")
} else {
println("이름이 초기화되지 않음!")
}
}
}
val user = User()
user.printName() // "이름이 초기화되지 않음!"
user.initUser()
user.printName() // "이름: 홍길동"
🔹 특징:
• lateinit은 var(mutable) 변수에만 사용 가능.
• Primitive 타입(Int, Double 등)에는 사용 불가능. (String, List 등 참조 타입만 가능)
• ::변수명.isInitialized로 초기화 여부 확인 가능.
🛠️ 2. lazy (자바의 싱글톤 및 getter 연산과 비교)
lazy 키워드는 val(불변) 변수에만 사용 가능하며, 해당 변수가 처음 사용될 때 초기화가 수행됨.
✅ Java 코드 (싱글톤 및 getter 활용)
class User {
private String name;
public String getName() {
if (name == null) {
System.out.println("초기화 수행됨!");
name = "이순신";
}
return name;
}
}
User user = new User();
System.out.println(user.getName()); // "초기화 수행됨!" 이후 "이순신" 출력
System.out.println(user.getName()); // "이순신" (이미 초기화됨)
🔹 특징:
• Java에서는 lazy가 없으므로 getter에서 null 체크를 통해 동적 초기화해야 함.
• Kotlin의 lazy를 활용하면 더 간결하고 효율적인 코드 작성 가능.
✅ Kotlin 코드 (lazy)
class User {
val name: String by lazy {
println("초기화 수행됨!")
"이순신"
}
}
val user = User()
println(user.name) // "초기화 수행됨!" 이후 "이순신" 출력
println(user.name) // "이순신" (이미 초기화되었으므로 다시 초기화되지 않음)
🔹 특징:
• lazy는 val(불변) 변수에만 사용 가능.
• 최초 접근 시점에서 한 번만 초기화되며 이후에는 캐싱됨.
• 자주 사용되지 않는 무거운 객체 초기화 시 유용함.
🔥 lateinit vs lazy 비교표
기능 | lateinit | lazy |
사용 가능 변수 타입 | var (mutable) | val (immutable) |
초기화 시점 | 직접 할당 시점 | 최초 접근 시점 |
null 허용 여부 | ❌ (String? 불가능) | ✅ 가능 (lazy { null } 가능) |
재할당 여부 | ✅ 가능 (나중에 값 변경 가능) | ❌ 불가능 (최초 값 유지) |
Primitive 타입 사용 가능 여부 | ❌ 불가능 | ✅ 가능 |
초기화 여부 확인 가능 여부 | ✅ (::변수명.isInitialized) | ❌ (불가능) |
예외 발생 가능성 | 초기화 전에 접근하면 UninitializedPropertyAccessException | 예외 없음 (사용 시 자동 초기화) |
일반적인 사용 사례 | DI, 객체 프로퍼티 초기화 | 무거운 객체의 지연 초기화 |
멀티스레드 안정성 | 보장되지 않음 (직접 관리) | 기본적으로 동기화됨 (lazy(LazyThreadSafetyMode.SYNCHRONIZED)) |
🛠️ 3. companion object (static과 비교)
Kotlin에서는 static 키워드가 없기 때문에, companion object를 사용하여 정적 멤버를 정의함.
✅ Java 코드 (static)
class User {
static final String DEFAULT_NAME = "Guest";
static User create() {
return new User();
}
}
System.out.println(User.DEFAULT_NAME); // "Guest"
User user = User.create(); // 정적 메서드 호출
🔹 특징:
• Java에서는 static을 사용하여 정적 멤버를 선언함.
• Kotlin의 companion object와 같은 역할을 함.
✅ Kotlin 코드 (companion object)
class User {
companion object {
val DEFAULT_NAME = "Guest"
fun create(): User {
return User()
}
}
}
println(User.DEFAULT_NAME) // "Guest"
val user = User.create() // 정적 메서드 호출
🔹 특징:
• companion object 내부의 변수/메서드는 클래스명으로 직접 접근 가능.
• Java의 static 멤버와 동일한 역할을 수행함.
🛠️ 4. companion object vs object
Kotlin에서 object는 싱글톤 객체를 만들 때 사용하며, companion object는 클래스 내부의 정적 멤버 역할을 수행함.
✅ companion object (클래스 내부에서 static 역할)
class User {
companion object {
fun create() = User()
}
}
val user = User.create() // 정적 메서드 호출
→ companion object는 static 역할을 수행하는 정적 메서드/변수를 포함하는 데 사용됨.
✅ object (싱글톤)
object Singleton {
val name = "Singleton Object"
}
println(Singleton.name) // "Singleton Object"
→ object는 프로그램 내에서 유일한 인스턴스를 가지는 싱글톤 객체를 만들 때 사용됨.
🔥 companion object vs object 비교표
기능 | companion object | object |
접근 방식 | 클래스 내부에서 정적 멤버 제공 | 싱글톤 객체 생성 |
사용 용도 | Java의 static 대체 | 글로벌 싱글톤 |
생성 방식 | 특정 클래스 내부에 정의 | 별도의 객체로 선언 |
인스턴스 생성 | 필요 없음 (User.create()) 가능 (클래스 인스턴스 생성 가능) |
필요 없음 (Singleton.name) 불가능 (이미 하나의 객체) |
접근 방식 | 클래스명.메서드() User.create() |
객체명.메서드() Singleton.name |
클래스 내부 사용 | ✅ 클래스 내부에서 정적 멤버처럼 사용 | ❌ 클래스 외부에서 바로 사용 |
생성자 사용 | ❌ 생성자 없음 | ❌ 생성자 없음 |
멀티톤 패턴 | ✅ 클래스마다 다르게 생성 가능 | ❌ (싱글톤만 가능) |
Object - 싱글톤 객체
특징:
✅ 클래스가 아니라 곧바로 하나의 객체
✅ 프로그램 실행 시 자동 생성되며, 전역적으로 사용 가능
✅ 생성자가 없음 (object는 인스턴스를 만들 수 없음)
✅ 자바에서는 static 멤버를 이용해 싱글톤을 구현해야 하지만, 코틀린에서는 object 키워드로 쉽게 가능
✅ 클래스가 아니라 곧바로 객체(Singleton) 자체를 생성하기 때문에, new 키워드 없이 사용 가능
companion object - 클래스 내부 정적 멤버
특징:
✅ 클래스 내부에 하나만 존재할 수 있음
✅ static처럼 정적 멤버를 선언하는 용도
✅ 클래스의 인스턴스를 생성하지 않아도 접근 가능 (클래스명.함수명())
✅ 하지만 object와 다르게, 클래스와 연결된 멤버처럼 동작
✅ 자바에서 static 키워드를 사용했던 방식 대신, 코틀린에서는 companion object를 활용하여 정적 멤버 선언 가능
✅ 클래스 내부에서 정적 메서드처럼 사용할 때 companion object가 적합
🎯 결론
• lateinit은 var 변수에 대해 초기화를 나중에 할 수 있도록 허용.
• lazy는 val 변수에 대해 최초 접근 시점에만 초기화 수행.
• companion object는 Java의 static을 대체하며, 정적 멤버를 포함.
• object는 싱글톤 패턴을 구현할 때 사용.
출처 : ChatGPT
'BE > Kotlin' 카테고리의 다른 글
[Kotlin] JPA에서 필드에 final을 붙이지 않는 이유 (val, var) (0) | 2025.02.21 |
---|---|
[Kotlin] 코틀린 문법 6 (open, 상속, 인터페이스) (0) | 2025.02.20 |
[Kotlin] 코틀린 문법 4 (apply, let, also, run, with) (0) | 2025.02.18 |
[Kotlin] 코틀린 문법 3 (filter, forEach, ?:, map, is, as) (0) | 2025.02.17 |
[Kotlin] 코틀린 문법 2 (0) | 2025.02.16 |