[Kotlin] Android 톺아보기 2-2. Kotlin의 변수와 자료형

이소은·2022년 9월 14일
0

Android 기본기 잡기

목록 보기
4/9

프로그래밍 언어의 가장 기본이자, 제일 처음 배우는 변수와 자료형에 대해 짚고 넘어가보자. 사실 프로그래밍을 한 번 쯤은 해본 사람이라면 모두 아는 내용이고, 지루할 수도 있는 내용이지만.. 혹시라도 놓치고 가는게 있을까 싶어 정리해보려 한다.


변수를 선언하고 자료형 추론하기

변수란 무엇일까? 변수는 값을 넣을 수 있는 상자에 비유할 수 있다. 다만, 이 상자는 값이 무엇인지에 따라 종류가 달라진다. 예로 정수형을 담을 지, 실수형을 담을 지, 문자열을 담을 지 값에 따라 상자의 타입이 달라지는 것이다.


변수는 val, var 두 가지 키워드를 이용해 선언할 수 있다.

  1. val
    • val로 변수를 선언하면 최초로 지정한 변수의 값으로 초기화 한 후, 더 이상 값을 바꿀 수 없다. 즉, 읽기 전용 변수가 되는 것이다.
  2. var
    • var로 변수를 선언하면 최초로 지정한 변수의 값으로 초기화를 한 후에도, 계속 값을 바꿀 수 있다. 즉, 쓰기가 가능한 변수인 것이다.

val userName: String = "Soeun"
val userName = "Soeun" // :String으로 자료형을 지정하지 않아도 "자료형을 String으로 추론함"

위의 코드 두번째 줄과 같이, Kotlin은 자료형을 지정해주지 않아도 변수를 선언하면 할당된 값을 통해 자료형을 알아서 지정한다. 이를 "자료형 추론"이라고 한다.


단, 아래 코드 예시와 같이 변수 선언 시 자료형을 지정하지 않고 값도 지정하지 않는다면 당연히 에러가 난다. 값이 없는 변수의 자료형을 추론할 길이 없기 때문이다.

var userName //이렇게는 안됨 (값도 없고, 자료형도 없고..)

따라서 변수를 선언 시에는

  1. var에 자료형 o 값 x
  2. var에 자료형 x 값 o
  3. var에 자료형 o 값 o
  4. val에 자료형 x 값 o
  5. val에 자료형 o 값 o

이러한 경우의 수만 가능하다.


변수 이름 짓기

변수 이름을 짓는데도 규칙이 필요하다. 많은 프로그래밍 언어가 공통적으로 갖는 규칙들이 있어, 한번 쯤은 읽어봤을 규칙이다. 규칙은 다음과 같다.

  1. 변수 이름은 숫자로 시작하면 안된다.
  2. 변수 이름에 예약어를 사용할 수 없다.
  3. 변수 이름은 의미있는 단어를 사용하되, camelCase를 따른다.

참조형 자료형? 기본형 자료형이 뭐야??

보통 프로그래밍 언어의 자료형은 참조형과 기본형으로 구별된다. 단, Kotlin은 참조형 자료형만 사용한다. 그렇다면 참조형과 기본형은 각각 무엇일까?


기본형은 Primitive Data Type으로, 순수한 자료형을 말하며 프로그래밍 언어에 내장되어 있다. 참조형은 Reference Type으로, 객체를 생성하고 동적 메모리 영역에 데이터를 둔 다음, 이 것을 참조하는 자료형이다. 참고로 Java는 int, float, double 등 기본형 + String, Date와 같은 참조형 두 가지를 모두 사용하지만 Kotlin은 참조형만 사용한다. (Kotlin에서 참조형으로 선언된 변수는 컴파일러가 다시 기본형으로 바꾸기 때문에 개발자가 이에 대한 최적화를 고려할 필요는 없다.)


기본형, 참조형의 동작 원리를 잠깐 보고 가자.

// java code
int a = 77;
Person person = new Person();

위의 예시에서 기본형으로 선언한 변수 a는 스택에 저장되며, 값이 저장된 메모리의 크기도 고정되어 있다. (자료형마다 고정된 메모리의 크기가 다름) 반면, 참조형 변수 person은 스택에는 값이 아닌 참조 주소가 있고, 실제 객체(값)는 동적 메모리인 힙에 저장된다.


보통 기본형이 참조형보다 코드 수행 시간이 더 빠른데, 참조형 자료형만 사용하는 Kotlin은 컴파일러가 기본형으로 바꿔주므로 자동으로 최적화를 수행한다.


어차피 컴파일러가 기본형으로 바꿀건데 왜 굳이.. 기본형을 같이 안쓰고 참조형만 사용할까?


프로그래밍 언어의 자료형에서 기본형은 null을 허용하지 않고, 참조형은 null을 허용한다. 잠시 Java와 비교해보면, Java에서는 primitive type과 boxed primitive type이 존재한다. primitive type은 stack 영역에 저장되는 반면, boxed primitive type은 내부적으로 primitive type을 갖는 wrapping된 class이므로 heap에 저장된다. (primitive type이 기본형이라면, boxed primitive type이 참조형인 셈이다.)


Java에서는 collection을 사용할 때나, 명시적으로 null을 변수에 주입해야 할 경우에 boxed primitive type을 사용해야 했다. 한마디로 Java에서는 개발자가 상황에 맞게 primitive type과 boxed primitive type을 고려하면서 개발해야 했던 것이다.


다시 Kotlin으로 돌아와서, Kotlin의 기본형과 참조형을 Java로 변환해보자. Kotlin에서 Int? 와 같이 null이 허용되는 자료형으로 선언했다면, 컴파일 시 Java의 Integer (boxed primitive type) 로 변환되어 참조형이 되고, Int와 같이 null이 허용되지 않는 자료형으로 선언했다면, 컴파일 시 Java의 int (primitive) 로 변환되어 기본형이 된다.


즉, Kotlin은 참조형만 사용한다! 기본형은 사용하지 않는다!!라고 하기는 어렵고 기본형과 참조형의 차이가 가려져 있다고 보면 될 것 같다. Kotlin에서는 Java와 달리 참조형을 쓸 지, 기본형을 쓸 지 개발자가 고민하지 않고 참조형만 사용해도, 컴파일러가 알아서 기본형과 참조형을 구분지어 변환하므로 개발의 편의성을 높인 부분이라 생각한다.


드디어 알아보는 Kotlin의 자료형

1. 정수 자료형

정수 자료형은 부호가 있는 것과 없는 것으로 구분된다. (단, Kotlin에서 부호가 없는 정수형은 실험적 기능이라 여기선 생략했다.)


부호가 있는 정수 자료형
자료형크기값의 범위
Long8바이트-2^63 ~ 2^63 -1
Int4바이트-2^31 ~ 2^31 - 1
Short2바이트-32768 ~ 32767
Byte1바이트-128 ~ 127

일반적으로 정수는 Int형으로 추론되며, 접두사나 접미사를 통해 2진수와 16진수도 표현 가능하다.

val num1 = 123 //Int형으로 추론
val num2 = 123L //접미사로 Long형으로 추론
val num3 = 0x0F //0x 접두사로 16진수 Int형으로 추론
val num4 = 0b00000001 //0b 접두사로 2진수 Int형으로 추론

Kotlin에서는 로 정수형의 자릿값을 구분할 수 있다. 를 사용해도 값에는 영향이 없으며, 원하는 위치 아무데나 넣을 수 있고 가독성을 위해 추가된 기능이다.

val num = 1_000_000

2. 실수 자료형

실수 자료형
자료형크기값의 범위
Double8바이트4.9E - 324 ~ 1.7E + 308
Float4바이트1.4E - 45 ~ 3.4E + 38

자료형을 명시하지 않으면 Double형으로 추론하며, Float로 지정하고 싶다면 접미사 F를 붙이면 된다.

val num1 = 3.14
val num2 = 3.14F // Float형

컴퓨터는 유한한 메모리에 무한히 많은 실수를 표현하기 위해서 부동 소수점 방식을 사용한다. 부동 소수점 방식은 실수를 가수와 지수로 나누어 표현하는 방식이다. 수학에서 3.14 X 10^16으로 나타낸 것을 컴퓨터로 나타내면 3.14E + 16이다. 이 뜻은 3.14에 10^16을 곱한다는 뜻으로 소수점이 오른쪽으로 16칸 이동하는 것이다. 반대로 0.0314는 3.14E -2로 나타내게 된다.


참고: 부동 소수점이 부정확한 이유?

32비트에서는 부동 소수점이 부호 (1비트) + 지수 (8비트) + 가수 (23비트) / 64비트에서 부동 소수점은 부호 (1비트) + 지수 (11비트) + 가수 (52비트)로 구성된다. 부호에는 0 혹은 1이 들어가는데, 0은 양수를 1은 음수를 의미한다. 지수는 실수의 지수를 의미하며, 가수는 표현하려는 수의 정수부분이 1이 되도록 정규화해서 저장한다.


예로 4바이트인 Float에서 -12.375를 나타낸다면, 부호 비트는 음수이므로 1이 되고 12.375는 2진수로 표현하여 1100.011(2)가 된다. 그런 다음 정수 부분이 1이 되도록 정규화 화면 1.100011 * 2^3으로 표현된다. 이때 정수부분은 항상 1이기에 생략하고 10011(2)만 23비트짜리 가수부분에 표현한다. 지수는 양수와 음수 모두 표현해야 하므로 8비트짜리는 -128 ~ 127까지 나타낼 수 있다.


위의 방식은 표현할 수 있는 비트에 제한이 있어 오차가 있어 주의해서 사용해야 한다. 예로 부동소수점으로 정의된 0.1을 1000번 반복해서 더하면, 수학적으로는 100이지만 결과는 100.09999999 이런 값이 나온다. 이는 0.1을 2진수로 표현하면 순환소수가 되고, 가수 부분이 부분적으로 잘려나가 부정확하기 때문이다.


3. 정수, 실수 자료형의 최소와 최댓값

각 자료형의 최소, 최댓값은 Byte.MIN_VAULE 혹은 Byte.MAX_VALUE 와 같은 property를 이용해서 출력할 수 있다. Kotlin은 각 자료형에서 표현할 수 있는 최소나 최댓값을 넘어가게 할당하면 오류를 발생시킨다. 다만, 비트 연산에서는 오류를 미리 내지 않는다. 즉, 비트 연산은 2의 보수 표현에 의해서 정해진 최소나 최대를 넘어가면 전혀 다른 값이 할당될 수 있어 주의해야 한다.


꿀팁!!! 2의 보수 알아보기

Byte형 변수는 최대가 127이다. 이 변수가 127일 때 1을 더하면 비트 연산에 의해 가장 상단의 비트인 부호 비트가 바뀌어 -128이 된다. 즉 01111111 + 1 = 10000000 = -128이 되는 것이다.

예로 -6을 표현하고 싶다면 6의 2진수값인 00000110을 뒤집어 11111001로 바꾼후 1을 더한 11111010이 된다.


4. 논리 자료형

논리 자료형은 true, false로 이루어지며 1비트 자료형이다. 흔히 검사 용도의 변수를 만들 때 주로 사용한다.


5. 문자 자료형

Char은 문자 1개를 표현하기 위해 사용하며 ''로 감싸서 표현한다. Char은 2바이트로 0 ~ 2^15-1만큼의 값을 나타낼 수 있다. 컴퓨터는 문자를 저장할 때 아스키코드나 유니코드 표를 보고서 해당 문자에 매칭되는 "숫자"를 저장한다. 즉, A를 저장하면 65로 이해하고 저장하는 것이다.

val ch = 'C'
val ch2: Char

그러나 65라는 숫자를 Char에 저장하는 것은 안된다. 무조건 문자 값으로 선언해야 하고, 이후에 문자 자료형에 숫자를 더해 다른 문자를 표현하는 것은 가능하다.

val ch = 'a'
println(ch+1) // b

val ch2: Char = 65 // error
val ch3: Char = 65.toChar() // 가능
val ch4 = 'ab' // error

6. 문자열 자료형

문자열 자료형은 여러 문자를 배열하여 저장할 수 있는 자료형이다. 문자 자료형인 Char은 기본형이지만, 문자열 자료형 String은 배열 형태로 된 참조형이다.

fun main(){
  var str1: String = "Hello"
  var str2 = "World"
  var str3 = "Helllo"
  
  println(str1===str2) // false
  println(str1===str3) // true
}

str1과 str3에는 같은 문자열이 저장되어 있는데, 이런 경우에는 "Hello"를 스택에 2번 저장하는 것보다 이미 저장된 값을 활용하는 것이 효율적이다. 따라서 Kotlin은 힙 영역의 String Pool이라는 공간에 "Hello"를 저장해두고 str1, str3이 같은 곳을 참조하도록 한다. 결과적으로 str1과 str3의 참조 주소가 같으므로 참조 비교 연산자인 ===의 결과로 true가 반환된다.


문자열에 변수값이나 표현식을 넣으려면 $를 사용하면 된다. 다음 예제와 같이 $뒤의 변수명에 해당 변수값이 들어가 문자열을 구성한다. 또한 ""나 ₩ 같은 문자를 표현하려면 앞에 이스케이프 문자인 백슬래시 \ 를 붙여서 표현해야 한다.

val speical = "\"Hello\", i have \$15"
val str2 = "a = ${a+2}"

자료형에 별명 붙이기

변수의 자료형이 복잡한 구조라면, 자료형에 typealias 키워드로 별명을 붙일 수 있다. 특정 자료형이나 클래스, 아주 긴 선언의 클래스를 별명을 이용해 짧게 명명할 수 있다.

typealias Username = String
val user: Username = "Soeun"

Reference

  • 이것이 안드로이드다 with Kotlin (고돈호 지음, 한빛미디어)
  • Do it! 코틀린 프로그래밍 (황영덕, 이지스퍼블리싱)
profile
안드로이드 개발자

0개의 댓글