| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 |
- List
- 안드로이드
- 리컴포지션
- kotlin
- 아두이노
- kotlinClass
- 코틀린
- Class
- 재정의함수
- Stack
- LinkedList
- 컴포즈
- Compoae
- 추상화함수
- Android
- 개발 보드
- 상태 호이스팅
- 우송대
- deque
- 스택
- 라즈베리파이 피코
- Queue
- 큐
- PICO4
- Kotlin자료구조
- State Hoisting
- 자료구조
- 클래스
- Kotlin LinkedList
- 우송대학교
- Today
- Total
개발자의 생활
object 키워드(정적 변수, 정적 함수) 본문
Kotlin에서는 static이라는 키워드 없습니다. 그 대신 새로운 object라는 키워드가 있습니다.
Java에서 사용되는 정적 static 키워드가 없다면 대신할 수 있는 그 이상의 새로운 무언가가 있어야 하죠 그게 object입니다. 키워드가 바뀌었다는 건 static 과는 다른 특징이 있다는 것이니 이번에는 object에 대해 알아보고 추가로 Kotlin에서의 정적 변수와 함수에 대해 알아보겠습니다.
이번 포스팅은 static과
1. 정적 변수
<Java>
public class Java {
public static void main(String[] args) {
Count count1 = new Count();
count1.countUp();
count1.countUp();
Count count2 = new Count();
count2.countDown();
System.out.println(Count.static_num + " | " + count1.num + " | " + count2.num);
}
}
class Count{
static int static_num = 0;
int num = 0;
void countUp(){
static_num++;
num++;
}
void countDown(){
static_num--;
num--;
}
}
Java에서는 간단하게 static키워드를 사용해서 정적 변수를 만들 수 있었습니다.
<Kotlin>
fun main(){
val count1 = Count()
val count2 = Count()
count1.countUp()
count1.countUp()
count2.countDown()
println("${Count.static_num} | ${count1.num} | ${count2.num}")
}
class Count {
var num: Int = 0
companion object {
var static_num: Int = 0
}
fun countUp() {
static_num++
num++
}
fun countDown() {
static_num--
num--
}
}
Kotlin에서는 companion object 키워드를 사용하고 내부에 변수를 만들면 정적 변수로 동작이 됩니다.
이번에는 Java에서 생성한 정적변수를 Kotlin에서 사용해 보고
반대로 Kotlin에서 생성한 정적변수를 Java에서 사용해 보겠습니다.
< Java -> Kotlin >
fun main(){
val count1 = Count()
val count2 = Count()
count1.countUp()
count1.countUp()
count2.countDown()
println("${Count.static_num} | ${count1.num} | ${count2.num}")
}
class Count{
static int static_num = 0;
int num = 0;
void countUp(){
static_num++;
num++;
}
void countDown(){
static_num--;
num--;
}
}
이와 같이 Java에서 생성한 정적변수는 를 코틀린에서 동일하게 그대로 사용할 수 있습니다.
< Kotlin -> Java >
public class Java {
public static void main(String[] args) {
Count count1 = new Count();
count1.countUp();
count1.countUp();
Count count2 = new Count();
count2.countDown();
System.out.println(Count.Companion.getStatic_num() + " | " + count1.getNum() + " | " + count2.getNum());
}
}
class Count {
var num: Int = 0
companion object {
var static_num: Int = 0
}
fun countUp() {
static_num++
num++
}
fun countDown() {
static_num--
num--
}
}
하지만 Kotlin에서 만든 정적변수를 Java에서 사용하려고 하면 Companion이라는 이름을 사용해서 접근을 해야 합니다.
여기서 짚고 넘어가야 할부분은 왜 클래스에 바로 접근하지 못하느냐입니다. 이를 알기 위해서는 JVM으로 컴파일했을 때 어떻게 변환되는지를 알아야 합니다.
public final class Count {
@NotNull
public static final Companion Companion = new Companion((DefaultConstructorMarker)null);
private int num;
private static int static_num;
public final int getNum() {
return this.num;
}
public final void setNum(int var1) {
this.num = var1;
}
public final void countUp() {
Companion var1 = Companion;
int var2 = static_num++;
int var3 = this.num++;
}
public final void countDown() {
Companion var1 = Companion;
int var2 = static_num;
static_num = var2 + -1;
int var3 = this.num;
this.num = var3 + -1;
}
public static final class Companion {
private Companion() {
}
public final int getStatic_num() {
return Count.static_num;
}
public final void setStatic_num(int var1) {
Count.static_num = var1;
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
이 코드는 Kotlin 코드를 JVM으로 컴파일하고 다시 Java로 디컴파일 해서 가져온 코드입니다. 즉 Kotlin 코드가 실제로 이렇게 동작을 하게 된다는 것입니다.
변환된 코드에서는 Companion이라는
그래서 Java에서 Companion라는 이름의 내부 클래스를 통해 접근하는 이유입니다.
추가적으로 Companion라는 이름은 기본이름 일 뿐이고 Kotlin에서 내부 클래스의 이름을 지정해 줄 수 있습니다.
companion object MyStatic{
var static_num: Int = 0
}
Count.MyStatic.getStatic_num()
그러면 Java에서는 MyStatic이라는 이름으로 접근할 수 있습니다.
1) @JvmStatic 사용하기
하지만 Java에서도 동일하게 Static으로 접근할 수 있는 방법이 있습니다.
companion object MyStatic{
@JvmStatic var static_num: Int = 0
}
Count.getStatic_num()
@JvmStatic을 붙이면 구조는 동일하지만 클래스 내부에 프로퍼티처럼(실제 프로퍼티는 아닙니다) 접근이 가능하도록 정적 메서드가 생성되고 내부적으로 Companion 클래스에 접근하는 방식의 코드가 만들어지게 되기 때문에 기존 Java와 동일한 방식으로 접근이 가능합니다.
2) @JvmField 사용하기
이 @JvmField는 실제 정적 필드에 접근을 하게 됩니다.
companion object MyStatic{
@JvmField var static_num: Int = 0
}
Count.static_num
@JvmStatic와 @JvmField의 차이점은 컴파일 시에 JvmStatic는 정적필드가 private으로 선언되어 정적메서드 만으로 접근해야 하고 JvmField 은 public으로 선언이 되기 때문에 외부에서도 접근이 가능하도록 한 것입니다.
2. 정적 메서드
이번에는 Companion Object에 메서드를 사용해 보겠습니다.
public class Count {
static void myMethod(){
System.out.println("정적 메소드");
}
}
fun main(){
Count.myMethod()
}
class Count {
companion object MyStatic{
fun myMethod (){
println("정적 메소드")
}
}
}
이렇게 일반함수를 생성하는 방식과 동일하며 클래스로 직접 접근하는 방식도 동일합니다.
Count.MyStatic.myMethod();
또한 정적 변수처럼 Kotlin에서 생성한 정적 메서드를 Java에서 사용하기 위해서는 중첩 메서드를 거쳐서 사용해야 합니다.
가짜 정적 메서드?
사실 Companion Object 내부에 선언한 메서드는 진짜 정적메서드가 아닙니다. 단지 Kotlin에서 정적으로 보일 뿐입니다.
public final class Count {
@NotNull
public static final MyStatic MyStatic = new MyStatic((DefaultConstructorMarker)null);
public static final class MyStatic {
private MyStatic() {
}
public final void myMethod() {
String var1 = "정적 메소드";
System.out.println(var1);
}
public MyStatic(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
위에 있던 Kotlin 코드를 디컴파일 한 코드입니다.
보시면 동일하게 myStatic 이름으로 중첩 클래스가 정적으로 생성이 됐고 내부에 myMethod 메서드가 있습니다. 하지만 메서드에는 static 키워드가 없습니다. 즉 myStatic 클래스는 싱클톤 패턴으로 하나만 생성이 되며 동일하게 내부 메서드도 하나만 생성이 되지만 JVM 입장에서는 단순히 인스턴스입니다.
진짜 정적 메서드 만들기
그럼 JVM 입장에서 정적메서드를 만드는 방법은 뭐가 있을까요? 방법은 2가지가 있습니다.
1번: @JvmStatic을 사용하기
2번: top level에서 선언하기
1번 @JvmStatic을 사용하기
이 방법은 정적변수를 사용할 때와 동일합니다. 실제 JVM컴파일러 가 정적 메서드를 사용할 수 있도록 기존 구조에서 바로 접근할 수 있는 정적 메서드를 추가하여 연결해 주게 됩니다.
public final class Count {
@NotNull
public static final MyStatic MyStatic = new MyStatic((DefaultConstructorMarker)null);
@JvmStatic
public static final void myMethod() {
MyStatic.myMethod();
}
public static final class MyStatic {
private MyStatic() {
}
@JvmStatic
public final void myMethod() {
String var1 = "정적 메소드";
System.out.println(var1);
}
public MyStatic(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
Count.myMethod();
이와 같이 Count 클래스 내부에 동일한 이름의 정적 메서드를 만들고 내부클래스에 접근하는 방식으로 구현되어 있습니다.
그래서 Java에서 동일하게 정적 메서드를 사용할 수 있게 됩니다.
2번 top level에서 선언하기
이 방법은 가장 간단하면서도 깔끔한 방식입니다.
fun myMethod(){
println("top level 메소드")
}
fun main(){
myMethod()
}
Kotlin에서는 최상단 파일에서 함수를 바로 정의할 수 있습니다.
그럼 top level에서 정의한 메서드는 자바에서 어떻게 사용할 수 있을까요? 이 부분도 Kotlin코드를 디컴파일 해서 확인해 보면 알 수 있습니다.
public final class KotlinKt {
public static final void myMethod() {
String var0 = "top level 메소드";
System.out.println(var0);
}
public static final void main() {
myMethod();
}
// $FF: synthetic method
public static void main(String[] args) {
main();
}
}
디컴파일 하게 되면 클래스가 만들어지고 내부에 top level에서 선언한 메서드가 들어 있습니다. 또한 main 메서드로 같이 있는데 이는 파일클래스 라고 합니다.
Kotlin에서는 파일당 하나의 최상위 클래스가 생성이 됩니다. 클래스명은 "파일명 + Kt"으로 생성이 됩니다. 그리고 내부에 모든 메서드는 static 메서드로 만들어집니다. 또한 public으로 만들어지므로 모든 곳에서 바로 접근이 가능합니다.
Kotlin에서는 메서드 명만 적어도 호출이 가능합니다. Java는 당연히 정적 메서드를 호출하는 방법으로 호출이 가능합니다.
추가로 변수 또한 top level에서 정의할 수 있으며 동일하게 적용됩니다.
만약 파일 클래스 명을 변경하고 싶다면
@file:JvmName("className")
위 어노테이션을 최상단에 적으시고 변경하고자 하는 문자열을 넣으시면 해당 문자열로 파일클래스가 만들어지게 됩니다.
3. 정리하기
지금까지 Kotlin에서 정적 변수와 정적 메서드를 사용하는 방법을 알아보았습니다.
그러면 어떤 방식을 사용하는 게 좋을까요 물론 정답은 없지만 저의 생각을 간단하게 적어 보겠습니다.
1. Companion object에서 정의하면 좋은 경우
- 특정 클래스와 강하게 연결되어 있는 경우
2. top level에서 정의하면 좋은 경우
- 특정 클래스와 강하게 연결되어 있지 않는 경우
- 전역에서 사용할 메서드인 경우
사실 object에 사용법이 한 가지 더 있지만 그 부분은 다음에 싱글톤 패턴을 포스팅할 때 올리도록 하겠습니다.
'kotlin' 카테고리의 다른 글
| data class (2) | 2025.09.12 |
|---|---|
| 중첩 클래스, 내부클래스 (0) | 2025.09.08 |
| 상속 (1) | 2025.08.22 |
| class(클래스) (4) | 2025.08.14 |
| 유용한 String 함수 (6) | 2024.10.30 |