# Table of Contents

# 자료형

자료형이란 데이터의 종류를 의미한다. 예를 들어 나이는 숫자고 이름은 문자열이다. 여기서 숫자와 문자열을 자료형이라고 한다. Kotlin에서는 이를 다음과 같이 표현한다.

var age: Int = 30
var name: String = "Paul"

Kotlin에서 제공하는 데이터 타입은 다음과 같다.

# 정수형 숫자

10진수 정수형 숫자는 다음과 같이 사용한다.

var age: Byte = 4       // 1 byte
var age: Short = 4      // 2 byte
var age: Int = 4        // 4 byte
var age: Long = 4       // 8 byte

2진수 정수형 숫자는 다음과 같이 사용한다.

var binaryByte: Byte = 0b01000001   // 10진수로 56

16진수 정수형 숫자는 다음과 같이 사용한다.

var hexadecimalByte: Byte = 0x41    // 10진수로 56

# 실수형 숫자

var height: Float = 178.2f  // 4 byte
var weight: Double = 78.3   // 8 byte

# 불리언

var isMarried: Boolean = true

# 문자

문자 한 개를 선언하고 초기화할 때는 따옴표(')를 사용한다.

var letter: Char = 'a'

# 문자열

문자열을 선언하고 초기화할 때는 쌍따옴표(")를 사용한다.

// 문자열 선언 및 초기화
var name: String = "Paul"

문자열 안에 변수를 삽입하는 것을 문자열 템플릿이라고 한다.

var description: String = "My name is ${name}"

# Java와 Kotlin의 데이터 타입 차이

Java에서는 자료형을 원시 타입과 참조 타입으로 나누어지며, 원시 타입은 클래스가 아니다.

// 원시 타입
int age = 30;
double height = 167.3;

// 참조 타입
Person person = new Person("Paul", "USA");

그러나 Kotlin에서는 원시 타입도 클래스로 정의되어있다.

val age: Int = 30;
val double: Double = 167.3;

val person: Person = Person("Paul", "USA")

# 타입 추론

Kotlin에서는 변수명 뒤에 자료형을 선언한다.

var name: String = "Paul"
var age: Int = 35

하지만 자료형을 선언하지 않아도 변수를 선언과 동시에 초기화하면 자동으로 자료형이 추론된다.

var name = "Paul"       // var name: String = "Paul"과 동일
var age = 35            // var age: Int = 35와 동일

# 변수와 상수

# 변수

변수를 선언할 때는 키워드var를 사용한다.

// 변수 선언
var name: String

// 변수 초기화
name = "Paul"
// 변수 선언과 동시에 초기화
var name: String = "Paul"

변수는 값을 초기화한 후 변경할 수 있다.

var name: String = "Paul"
name = "James"

# 상수

상수를 선언할 때는 키워드 val을 사용한다.

// 상수 선언과 초기화
val name: String = "Paul"

상수는 초기화한 후 값을 변경할 수 없다.

name = "James"       // Error

상수는 선언과 초기화를 동시에 해야한다.

val nation: String = "Republic of Korea"    // Success

val name: String    // Error
name = "Son"        // Error

# val vs. const

val런타임에 값이 결정되는 상수다. 상수는 한 번 값이 할당되면 변경할 수 없다.

// 기본타입의 상수 선언
val age: Int = 30

// 에러, 값 변경 불가능
age = 31

val은 문자열도 할당할 수 있다.

// 문자열 타입의 상수 선언
val name: String = "Paul"

// 에러, 값 변경 불가능
name = "John"

val는 클래스의 인스턴스도 할당할 수 있다.

val person = Person("Paul", 35)

// 에러, 물론 다른 인스턴스를 할당할 수도 없다.
person = Person("John", 25)

반면 const val컴파일 타임에 값이 결정되는 상수다. 인스턴스를 할당할 수 없으며, 기본 자료형과 문자열만 할당할 수 있다. 이름은 통상 대문자언더바를 통해 선언한다.

const val DEVICE_WIDTH: Int = 1400                  // 성공
const val URL: String = "http://www.naver.com"      // 성공

const val person: Person = Person("ronaldo", 34)    // 에러

클래스 안에서 사용할 때는 companion object 블럭 안에 정의한다.

class MediaQueryMananger {
    companion object {
        const val DEVICE_WIDTH: Int = 1400
    }
}
 
// 클래스의 인스턴스를 생성하지 않고 사용할 수 있다.
println(MediaQueryManager.DEVICE_WIDTH)

# 늦은 초기화

Kotlin에서 변수나 상수는 원칙적으로 선언과 동시에 초기화해야 한다. 변수는 초기화하지 않으면 컴파일 자체가 안되기 때문이다.

// 기본 데이터타입 Int
var age: Int        // Property must be initialized or be abstract
var person: Person  // Property must be initialized or be abstract

값이 없다면 데이터 타입을 Nullable로 선언하고 null이라도 할당해야 한다.

var age: Int? = null
var person: Person? = null

그러나 Kotlin은 선언만 먼저 하고 초기화는 나중에 하는 방법을 제공한다. 이를 늦은 초기화(Late Initialization)라고 한다.

늦은 초기화에는 두 가지 방법이 있다.

  1. lateinit var
  2. by lazy

# lateinit var

lateinit var을 사용하면 변수만 먼저 선언하고 초기화는 특정 시점으로 미룰 수 있다. 기본 자료형은 사용할 수 없으며 클래스의 인스턴스만 가능하다.

class Person(name: String, age: Int)

lateinit var person: Person     // 컴파일 에러가 발생하지 않는다.

person = Person("Paul", 35)

문자열 String도 클래스이므로 사용할 수 있다.

lateinit var name: String
name = "Ronaldo"

보통 안드로이드 앱을 개발할 때 다음과 같이 사용한다.

class MainActivity : AppCompatActivity() {

    private lateinit var loginButton: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        loginButton = findViewById(R.id.login_button)

        loginButton.setOnClickListener {
            println("LoginButton Clicked.")
        }
    }
}

# by lazy

키워드 by lazy를 상수 앞에 붙이면 상수에 접근할 때 값이 초기화된다. 접근하기 전에는 값이 초기화되지 않으므로 메모리를 절약할 수 있다.

val name: String by lazy {
    println("Initializing....")
    "Paul"  // 블럭의 마지막 값이 할당된다.
}

print(name) // 상수에 접근하는 이 시점에 상수 name이 초기화됩니다.

보통 안드로이드 앱을 개발할 때 다음과 같이 사용한다.

class MainActivity : AppCompatActivity() {

    private val loginButton: Button by lazy { findViewById<Button>(R.id.login_button) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        loginButton.setOnClickListener {
            println("LoginButton Clicked.")
        }
    }
}