Languege/Kotlin / / 2025. 3. 10. 23:24

[Kotlin Basic] 기초 문법 학습 4

이번 포스팅에서는 코틀린에서 자바와 달리 어떻게 object 키워드와 static(정적) 멤버, companion object, Singleton, 익명 클래스, 중첩 클래스, 그리고 Data / Enum / Sealed Class 등을 구현하는지 정리해보겠습니다.


1. object 키워드와 static 함수/변수

자바에서는 static 키워드를 사용하여 정적 함수를 만들고, 클래스가 로딩될 때 메모리에 한 번만 올라갑니다.

Java

public class JavaPerson {
  private static final int MIN_AGE = 1;

  public static JavaPerson newBaby(String name) {
    return new JavaPerson(name, MIN_AGE);
  }

  private String name;
  private int age;

  private JavaPerson(String name, int age) {
    this.name = name;
    this.age = age;
  }
}

코틀린에는 static 키워드가 없고, 대신 companion object를 통해 비슷한 동작을 합니다.

Kotlin

class Person private constructor(
    var name: String,
    var age: Int
) {
    companion object {
        private val MIN_AGE = 1

        fun newBaby(name: String): Person {
            return Person(name, MIN_AGE)
        }
    }
}
  • 클래스 내부의 companion object는 클래스와 함께 하나만 존재하는 “동행 객체”로서, 자바의 static 멤버처럼 사용됩니다.

2. companion object

companion object는 클래스와 동행하는 하나의 객체입니다. 이름을 붙일 수도 있고, 인터페이스를 구현할 수도 있습니다.

interface Log {
    fun log()
}

class Person private constructor(
    val name: String,
    val age: Int
) {
    companion object Factory : Log {
        private const val MIN_AGE = 1

        fun newBaby(name: String): Person {
            return Person(name, MIN_AGE)
        }

        override fun log() {
            println("companion object 는 인터페이스를 구현할 수 있다. 또한 여기에 선언된 상수 값은 $MIN_AGE 이다.")
        }
    }
}
  • val과 const val:
    • val은 런타임 시 최초 할당되고 값이 변경되지 않음.
    • const val은 컴파일 시점에 값을 확정하는 ‘진짜 상수’로, 기본 타입과 String에만 사용 가능.

3. Singleton

자바에서 싱글톤 패턴을 구현하려면, private 생성자와 static 인스턴스를 보통 사용합니다.

Java

public class JavaSingleton {
  private static final JavaSingleton INSTANCE = new JavaSingleton();

  private JavaSingleton() { }

  public static JavaSingleton getInstance() {
    return INSTANCE;
  }
}

코틀린에서는 object 키워드만 사용하면 곧바로 싱글톤을 만들 수 있습니다.

Kotlin

object Singleton
  • 생성자 호출 없이 별도 파일이나 코드에 object Singleton 선언만 하면 됩니다.

4. 익명 클래스

자바와 코틀린 모두 일회성 인스턴스 생성을 위해 익명 클래스를 활용할 수 있습니다.

Java

public interface Movable {
  void move();
  void fly();
}

public static void main(String[] args) {
  moveSomething(new Movable() {
    @Override
    public void move() {
      System.out.println("움직인다.");
    }

    @Override
    public void fly() {
      System.out.println("난다.");
    }
  });
}

private static void moveSomething(Movable movable) {
  movable.move();
  movable.fly();
}

Kotlin

interface Movable {
    fun move()
    fun fly()
}

fun main() {
    moveSomething(object : Movable {
        override fun move() {
            println("움직인다.")
        }

        override fun fly() {
            println("난다.")
        }
    })
}

private fun moveSomething(movable: Movable) {
    movable.move()
    movable.fly()
}
  • 코틀린은 object : 인터페이스명 { … } 를 사용하여 익명 클래스를 만듭니다.
  • 인터페이스의 메서드를 구현할 때 override 키워드를 꼭 써야 합니다.

5. 중첩 클래스 (Nested Class)

클래스 안에 클래스를 선언할 때, 바깥 클래스를 참조하는지 여부에 따라 방식이 달라집니다.

Java

public class JavaHouse {
  private String address;
  private LivingRoom livingRoom;
  private BedRoom bedRoom;

  public JavaHouse(String address) {
    this.address = address;
    this.livingRoom = new LivingRoom(10);
    this.bedRoom = new BedRoom(20);
  }

  // Inner Class (바깥 클래스 참조 가능)
  public class LivingRoom {
    private double area;
    public LivingRoom(double area) {
      this.area = area;
    }
    public String getAddress() {
      return JavaHouse.this.address;
    }
  }

  // static Inner Class (바깥 클래스 참조 불가)
  public static class BedRoom {
    private double area;
    public BedRoom(double area) {
      this.area = area;
    }
  }
}

Kotlin

class House(
    private val address: String
) {
    // 바깥 클래스를 참조하려면 inner 키워드가 필요함
    inner class LivingRoom(
        private val area: Double
    ) {
        val address: String
            get() = this@House.address
    }

    // 기본 선언 시, 바깥 클래스와 무관 (static inner 와 유사)
    class BedRoom(
        private val area: Double
    )
}
  • 코틀린에서는 기본적으로 중첩 클래스를 선언하면 바깥 클래스의 참조가 없는 static inner 형태.
  • 바깥 클래스를 참조하려면 inner 키워드를 반드시 사용해야 합니다.

6. Data Class

DTO나 값을 저장하는 목적의 클래스를 작성할 때, 자바는 생성자와 getter/setter, equals, hashCode, toString 등을 직접 만들어야 합니다.

Java

public class JavaPersonDto {
  private final String name;
  private final int age;

  public JavaPersonDto(String name, int age) {
    this.name = name;
    this.age = age;
  }

  // Getter, equals, hashCode, toString ...
}

코틀린에서는 data class 한 줄로 해결할 수 있습니다.

Kotlin

data class PersonDto(
    val name: String,
    val age: Int
)
  • equals, hashCode, toString, componentN, copy 메서드를 자동 생성합니다.

7. Enum

열거형 상수를 정의한 뒤, 각각을 싱글톤으로 사용할 수 있습니다. 자바와 코틀린 모두 문법이 비슷하나, 코틀린에서는 when 구문과 결합해 더 깔끔하게 처리 가능합니다.

Java

public enum JavaCountry {
  KOREA("KO"),
  AMERICA("US");

  private final String code;

  JavaCountry(String code) {
    this.code = code;
  }

  public String getCode() {
    return code;
  }
}

Kotlin

enum class Country(
    private val code: String
) {
    KOREA("KO"),
    AMERICA("US");
}

fun handleCountry(country: Country) {
    when (country) {
        Country.KOREA -> println("한국 처리")
        Country.AMERICA -> println("미국 처리")
    }
}
  • 코틀린의 when 구문은 모든 enum 값을 검사하므로, else 없이도 컴파일러가 빠진 케이스가 있는지 체크할 수 있습니다.

8. Sealed Class

“봉인된” 추상 클래스로, 외부에서 임의로 상속할 수 없습니다. 컴파일 시점에 모든 하위 클래스를 알고 있어 when 구문과 함께 사용하기 좋습니다.

Kotlin

sealed class HyundaiCar(
    val name: String,
    val price: Long
)

class Avante : HyundaiCar("아반테", 1000L)
class Sonata : HyundaiCar("소나타", 2000L)

fun handleCar(car: HyundaiCar) {
    when (car) {
        is Avante -> println("아반테 처리")
        is Sonata -> println("소나타 처리")
    }
}
  • enum과 달리 멀티 인스턴스를 생성할 수 있고, 클래스 상속 구조를 확장할 수 있습니다.
  • 단, 같은 패키지 내에서만 하위 클래스를 만들 수 있습니다.

✏️ 마무리 정리

오늘은 코틀린에서 자바와 다르게 구현하는 여러 가지 개념들을 살펴봤습니다.

  1. 자바 static 대신 코틀린 companion object를 사용
  2. object 키워드만으로 간단히 Singleton 구현
  3. 익명 클래스는 object : 인터페이스 형태
  4. 중첩 클래스는 기본적으로 static inner, inner 키워드를 써야 바깥 참조
  5. data class로 DTO 자동 구성
  6. enum과 when 구문이 만나면 모든 케이스를 안전하게 처리
  7. sealed class로 컴파일 시점에 하위 타입을 모두 파악

다음에도 코틀린을 배우면서 자주 헷갈리는 내용을 정리해보겠습니다. 이제 5개의 챕터만 남아서 마지막까지 파이팅해보겠습니다.
읽어주셔서 감사합니다!

'Languege > Kotlin' 카테고리의 다른 글

[Kotlin Basic] 기초 문법 학습 5  (0) 2025.03.12
[Kotlin Basic] 기초 문법 학습 3  (1) 2025.03.06
[Kotlin Basic] 기초 문법 학습 2  (0) 2025.03.04
[Kotlin Basic] 기초 문법 학습 1  (0) 2025.03.03