Kotlin에는 static 키워드가 없다.
🧐 그럼 코틀린에선 static을 대체하기 위해 무엇을 써야 할까?
우선, 안드로이드에선 정적(static) 변수/메서드를 사용하는 경우가 보통은 아래와 같다.
- Activity, Fragment의 인텐트 extra로 사용하는 키
- Log 출력을 위한 Tag 이름 정의
- View 내부에서 사용하는 고정된 길이 값 (너비, 높이 등)
- 각종 유틸리티 클래스 내 메서드 (빠른 계산을 요구하는...등)
🖐 패키지 내에 함수를 선언해서 사용할 수 있다!
코틀린에서 static을 대체하기 위한 옵션은 아래와 같다.
- Top-level functions
- Top-level constants
- Companion object functions
- Object instance
코틀린의 1급 객체는 함수니까..😁
그럼 하나씩 알아보자.
자바에선 main 함수 작성시 많은 보일러 플레이트 코드가 있다.
클래스 안에 public static void main(String[] args)
와 같이 작성해야 한다.
코틀린에선 이를 Top-level function으로 구현해주면 된다.
많은 Util, Helper 클래스를 작성해본적이 있다 분명.
Util 클래스 안에 static 키워드로 범용적인 메소드를 구현하였다.
Kotlin에서는 이를 Top-level function으로 작성해주면 된다.
그리고 해당 패키지 내부에서 사용하거나 다른 패키지에서 해당 Top-level function을 import해서 사용 가능하다.
[JAVA]
public class StringUtils {
private StringUtils() { /* Forbid instantiation of utility class */ }
public static int lowerCaseCount(String value) {
return value.chars().reduce(0,
(count, current) -> count + (Character.isLowerCase(current) ? 1 : 0));
}
}
[Kotlin]
package toplevel
fun lowerCaseCount(value: String): Int = value.count { it.isLowerCase() }
property 또한 top-level에 작성 가능하다.
물론, immutable하게 선언해야 한다.
[JAVA]
class Foo{
public static final String BAR = "bar";
}
[Kotlin]
const val BAR = "bar"
Get rid of utility classes에서 만든 lowerCaseCount 메소드가 한 클래스에서만 필요하다면 Java의 경우 아래와 같다.
public class MyClass {
private String myString;
public void doSomething() {
int count = lowerCaseCount(myString);
...
}
private static int lowerCaseCount(String value) { ... } // private static pure function
}
코틀린에서는 해당 static 메소드를 클래스 밖으로 빼서 해당 파일 내에 private top-level function으로 구현하여 해당 파일 내에서만 구현 가능하게 할 수 있다.
코틀린에서는 싱글톤 패턴을 Object 키워드로 제공한다.
그래서, 자바에서 싱글톤 패턴 구현 시 static 키워들 썼다면 Object 키워드로 대체할 수 있다!
static 메소드에서 Non-static method에 접근해야 하는 경우엔 어떻게 해야할까?
이럴 땐 Companion object를 사용하면 된다.
(static에 대한 대체로 무조건적으로 Companion Object을 사용하는 사람들이 있는데 매우 잘못된...어 난데)
Companion objects는 클래스 내부에 정의된 Singleton value이다.
Companion object 내 멤버들은 클래스 이름으로 호출할 수 있다.
게다가 Companion object는 클래스 인스턴스의 private 멤버들에 접근 할 수 있기 때문에 팩토리 메소드에 특히 유용하다!
class Rocket private constructor() {
private fun attachPropellers() { ... }
companion object {
fun build(): Rocket {
val rocket = Rocket() // can call private constructor
rocket.attachPropellers() // can call private function
return rocket
}
}
}
fun main() {
val rocket = Rocket.build() // Companion function called using the accompanied class name
}
우선, 안 좋은 예제 부터 보자.
(보통 이렇게 쓰는 사람들이 많다..어 나도)
object SampleUtils{
fun jsonString(map : mutableMap) : String{
return objectMapper.writeValueAsString(map)
}
}
//사용할 땐 아래처럼..
val map : MutableMap<String, Any?> = mutableMapOf("K" to "V")
SampleUtils.jsonString(map) // {"K":"V"}
fun mutableMap.jsonString() : String = objectMapper.writeValueAsString(this)
//사용은 아래 처럼
val map : MutableMap<String, Any?> = mutableMapOf("K" to "V")
map.jsonString()