지역변수(Local variable)
기본개념: 지역변수는 중괄호 내부에서 선언되어 함수 내부에서만 접근이 가능하며, 함수나 블록의 실행이 끝나면 메모리에서 자동으로 해제된다.
fun exampleFunction() {
val localVar = "I am a local variable"
println(localVar) // 함수 내부에서만 접근 가능
}
// println(localVar) // 오류: localVar는 exampleFunction 밖에서 접근할 수 없습니다.
전역변수(Global Variable)
기본개념: 지역변수와 달리 중괄호 외부에서 선언되는 변수로, 어디서든지 참조하여 사용할 수 있다.
파일 최상단에 변수를 선언하면 그 파일 내에서 어디서든 접근이 가능하게 된다.
val globalVar = "I am a global variable"
fun exampleFunction() {
println(globalVar) // 모든 함수에서 접근 가능
}
fun anotherFunction() {
println(globalVar) // 모든 함수에서 접근 가능
}
정적변수(Static variable)
정적으로 할당되는 변수이며, 프로그램 실행 전반에 걸쳐 변수의 수명이 유지되는 것입니다.
멤버가 정적(static)으로 선언되면, 해당 클래스의 모든 객체에 대해 하나의 데이터만이 유지 관리됩니다.
하지만 코틀린에는 정적(static) 변수 혹은 메소드가 없다 !
자바와 다르게 Kotlin에선 static 이라는 예약어를 사용하지 않는다.
우선 static에 대해서 정리해보자.
Java static 이란?
정적(static)은 고정된이란 의미를 가지고 있습니다. Static이라는 키워드를 사용하여 Static변수와 Static 메소드를 만들 수 있는데 다른 말로 정적 필드와 정적 메소드라고도 하며 이 둘을 합쳐서 정적멤버라고 한다.(혹은 클래스 멤버라고도 한다.) 정적 필드와 정적 메소드는 객체(인스턴스)에 소속된 멤버가 아니라 클래스에 고정된 멤버이다. 그렇기에 클래스 로더가 클래스를 로딩해서 메소드 메모리 영역에 적재할 때 클래스 별로 관리한다. 따라서 클래스의 로딩이 끝나는 즉시 바로 사용이 가능하다.
public class Main {
static int N; // 클래스필드
public static void main(String[] args) { // 클래스 메소드
// TODO Auto-generated method stub
}
}
정적 메소드는 클래스가 메모리에 올라갈 때 정적 메소드가 자동적으로 생성된다. 그렇기에 정적 메소드는 인스턴스를 생성하지 않아도 호출을 할 수 있습니다. 정적 메소드는 유틸리티 함수를 만드는데 유용하게 사용한다.
그렇다면 Kotlin에도 Java의 static과 비슷한 것이 있지 않을까?
있다!
Object와 Companion Object다 하나하나씩 정리한 이후에 둘의 차이점을 비교해보자.
Kotlin Object
코틀린에는 독특한 싱글턴(singleton : 인스턴스가 하나만 있는 클래스) 선언 방법이 있다. 그것이 바로 Object이다.
코틀린에서는 object 키워드를 사용해서 별다른 정의 없이 싱클톤 구현을 지원해준다.
자바에서 클래스 내부에 static 객체로 한번만 할당해주던 코드를 코틀린에서는 object 키워드를 쓴 클래스로 구현해서 static 객체에 할당해주는 것 처럼 자동으로 생성해준다.
싱글톤이란?
싱글톤은 인스턴스가 하나만 있는 클래스를 의미한다.
object CarFactory {
val cars = mutableListOf<Car>()
fun makeCar(horsepowers: Int): Car {
val car = Car(horsepowers)
cars.add(car)
return car
}
}
class Car(power: Int) {
}
object로 싱글턴 클래스를 정의할 수 있습니다. 아래 코드에서 CarFactory 클래스를 정의할때 class가 있어야 할 위치에 object를 입력해주면 이 클래스는 싱글턴으로 동작하게 됩니다.
아래 코드처럼 CarFactory.makeCar 처럼 메소드에 접근하여 Car객체를 생성할 수 있습니다. 또한, CarFactory.cars 처럼 직접 변수에 접근할 수 있습니다. CarFactory 객체는 싱글턴으로 구현이 되었기 때문에 여러번 호출해도 CarFactory 객체는 한번만 생성이 됩니다.
위와같은 방법을 우리는 선언식이라고 한다.
하지만 표현식으로 사용하면 object는 singleton의 형태로 사용하지 않는다.
주로 다음과 같은 형태로 사용한다.
1. 익명의 클래스의 객체를 바로 생성하고자 할 때
2. 추상클래스, 인터페이스의 구현체를 익명 클래스의 객체로 구현하고자 할 때
// 1. 익명 클래스의 객체를 바로 생성하고자 할 때
val user = object {
val name = "hunseong"
val age = 24
}
println(user.name) // hunseong
println(user.age) // 24
// 2. 추상클래스, 인터페이스의 구현체를 익명 클래스의 객체로 구현하고자 할 때
val myListener: MyInterface = object : MyListener {
//implement interface
}
user.addListener(myListener)
user2.addListner(object : MyListener {
//implement interface
})
Companion Object
Companion Object란?
자바의 static을 없애고 kotlin에서는 Compainon Object라는 동반 객체를 사용하여 정적 멤버를 정의한다. 또한 Companion에 이름을 설정하여 커스텀한 Companion Object를 만들 수 있다.
- companion object (동반 객체)는 이름 그대로 클래스 내부에 존재하며, 클래스가 메모리에 적재될 때 함께 생성된다.
- 동반 객체라는 이름답게, 클래스 별 하나의 companion object만을 둘 수 있다.
- object와 같이 클래스, 인터페이스를 상속 받을 수 있다.
- Outer class는 companion object에 접근 가능하나, companion object 내에서는 Outer class에 접근할 수 없다.
- 별도 클래스의 인스턴스를 생성하지 않고 클래스명으로 접근하여 자바의 static 변수, 메소드처럼 사용할 수 있다.
- 클래스명.Companion 으로 접근 할 수 있으며, 변수에 할당이 가능하다.
class User {
companion object {
val TAG = "user_tag"
fun logName(name: String) {
Log.d(TAG, "user name : $name")
}
}
}
println(User.TAG) // user_tag
User.logName("hunseong") // log - user name : hunseong
val userObject = User.Companion
println(User.Companion.TAG) // user_tag
println(userObject.TAG) // user_tag
하지만 자바의 static과는 차이가 있는데,
Companion object는 static과 달리 객체로써 생성이 된다는 것이다.
그렇다면 companion object 내 변수는, 메서드는 static이 될 수 없는 것일까?
아니다
해당 companion object 내 변수, 메서드에 각각 @JvmField, @JvmStatic 어노테이션을 붙이면 static 키워드가 붙으므로
Java의 static 변수, 메서드와 같이 사용 할 수 있다.