Kotlin 공식 문서 살펴보기 (5) - Basic types
24 Jan 2022숫자
정수
type | size(bits) | 최소값 | 최대값 |
---|---|---|---|
Byte |
8 | -128 | 127 |
Short |
16 | -32768 | 32767 |
Int |
32 | -2,147,483,648(-2^31) | 2,147,483,647( 2^31 -1) |
Long |
64 | -9,223,372,036,854,775,808 (-2^63) | 9,223,372,036,854,775,807 (2^63 - 1) |
Int
의 최대값을 초과하지 않는 정수 값으로 초기화된 모든 변수는 추론된 Int
타입을 가진다. 만약 초기화 값이 이 값을 초과하면 타입은 Long
이 된다. Long
타입을 명화갛게 하고 싶을 때는 L
접미사를 붙이도록 한다.
val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1
부동 소수점
부동 소수점 타입으로 Float
와 Double
가 있다. IEEE 754에 따르면 부동 소수점 타입은 소수 자릿수에 따라 다르다. Float
은 단정밀도를, Double
은 배정밀도에 해당한다.
type | size(bits) | 가수부 | 지수부 | 10진수 |
---|---|---|---|---|
Float |
32 | 24 | 8 | 6-7 |
Double |
64 | 53 | 11 | 15-16 |
Double
과 Float
은 소수가 있는 숫자로 초기화 할 수 있으며 .
으로 구분된다. 컴파일러는 Double
타입으로 추론한다.
val pi = 3.14 // Double
// val one: Double = 1 // Error
val oneDouble = 1.0 // Double
Float
타입을 명시하고 싶을 때는 f
또는 F
접미사를 붙인다. 만약 그 값에 6-7자리 이상의 10진수가 포함되면 반올림된다.
val e = 2.7182818284 // Double
val eFloat = 2.7182818284f // Float, 실제 값은 2.7182817
Kotlin에서는 다른 언어와 다르게 숫자에 대해 암시적으로 변환해주지 않는다.
fun main() {
fun printDouble(d: Double) { print(d) }
val i = 1
val d = 1.0
val f = 1.0f
printDouble(d)
// printDouble(i) // Error: Type mismatch
// printDouble(f) // Error: Type mismatch
}
리터럴 상수
-
10진수:
123
-
Long 타입:
123L
-
16진수:
0x0F
-
바이너리:
0b00001011
-
Double 타입:
123.4
,123.5e10
-
Float 타입:
123.4f
-
_
사용 가능val oneMillion = 1_000_000 val creditCardNumber = 1234_5678_1352_3823L val socialSecurityNumber = 999_99_9999L val hexBytes = 0xFF_EC_DE_5E val bytes = 0b11010010_01101001_10010100_10010010
JVM 숫자 표현
JVM에서는 숫자는 원시형으로 저장되지만, Int?
같은 nullable한 숫자 참조를 생성하거나 제네릭을 사용할 때는 예외이다. 이런 경우에는 Integer
, Double
등의 Java 클래스에 박싱된다.
val a: Int = 100
val boxedA: Int? = a
val anotherBoxedA: Int? = a
val b: Int = 10000
val boxedB: Int? = b
val anotherBoxedB: Int? = b
println(boxedA === anotherBoxedA) // true
println(boxedB === anotherBoxedB) // false
위의 예제에서 a에 대한 모든 nullable 참조는 같은 객체이다. 이는 -128 ~ 127 사이의 Integer
에 JVM이 적용하는 메모리 최적화 때문이다. b에는 적용되지 않으므로 모두 다른 객체이다.
val b: Int = 10000
val boxedB: Int? = b
val anotherBoxedB: Int? = b
println(boxedB == anotherBoxedB) // true
명시적 변환
모든 숫자형 타입은 다른 타입으로 변환할 수 있다.
toByte()
toShort()
toInt()
toLong()
toFloat()
toDouble()
toChar()
연산
+
, -
, *
, /
, %
의 표준 산술 연산을 제공하며 사용자 정의 클래스에서 오버라이드 할 수 있다.
println(1 + 2)
println(2_500_000_000L - 1L)
println(3.14 * 2.71)
println(10.0 / 3)
-
나눗셈
- 정수의 나눗셈에서 소수는 버려진다.
val x = 5 / 2 println(x == 2) // true val y = 5L / 2 println(y == 2L) // true
- 부동 소수점 타입을 반환하기 위해서는 인수 중 하나를 부동 소수점 타입으로 명시적으로 변환해야 한다.
val x = 5 / 2.toDouble() print(x == 2.5) // true
-
비트 연산
- 정수에서 비트 연산 제공
- 바이너리 레벨에서 작용
Int
,Long
에만 적용
val x = (1 shl 2) and 0x000FF000
shl(bits)
: 부호있는 왼쪽 시프트shr(bits)
: 부호있는 오른쪽 시프트ushr(bits)
: 부호없는 오른쪽 시프트and(bits)
: 비트 andor(bits)
: 비트 orxor(bits)
: 비트 xorinv()
: 비트 반전
- 부동소수점 비교
a == b
&a != b
a < b
,a > b
,a <= b
,a >= b
- 범위 인스턴스화 & 범위 확인:
a..b
,x in a..b
,x !in a..b
- 부호 없는 정수
UByte
: 0 ~ 255 사이의 부호 없는 8비트 정수UShort
: 0 ~ 65535 사이의 부호 없는 16비트 정수UInt
: 0 ~ 2^32 - 1 사이의 부호 없는 32비트 정수ULong
: 0 ~ 2^64 -1 사이의 부호 없는 64비트 정수
- 부호 없는 배열 및 범위
UByteArray
: 부호 없는 byte 배열UShortArray
: 부호 없는 short 배열UIntArray
: 부호 없는 int 배열ULongArray
: 부호 없는 long 배열
-
Literal
- 부호 없는 정수를 더 쉽게 사용하기 위해 정수 리터럴에 태그 지정
u
,U
,uL
,UL
로 부호 없는 리터럴 태그
val b: UByte = 1u val s: UShort = 1u val l: ULong = 1u val a1 = 42u val a2 = 0xFFFF_FFFF_FFFFu val a = 1UL
Boolean
true
,false
값을 가짐- Nullable 을 대응할 수 있는
Boolean?
제공 - 빌트인 연산자
||
: 논리적 OR&&
: 논리적 AND!
: 논리적 NOT
||
와&&
은 느리게 동작(work lazily)
val myTrue: Boolean = true
val myFalse: Boolean = false
val boolNull: Boolean? = null
println(myTrue || myFalse) // true
println(myTrue && myFalse) // false
println(!myTrue) // fasle
Characters
Char
로 표현- 작은따옴표(
'
) 사용 - 특수 문자는
\
로 시작 - 다른 문자를 인코딩 할 때는 유니코드 이스케이스 시퀀스 사용
val aChar: Char = 'a'
println(aChar)
println('\n')
println('\uFF00')
문자열
- 쌍따옴표(
"
) 사용 - 문자열의 요소는 인덱스로 접근 가능
s[i]
- for loop로 문자 반복 가능
for (c in str) {
println(c)
}
- 문자열은 변경할 수 없다(immutable)
- 초기화하면 값을 바꾸거나 새 값을 할당할 수 없음
- 문자열을 변환하는 모든 작업은 새
String
객체를 반환하고 원래 문자열을 변하지 않게 함
val str = "abcd"
println(str.uppercase()) // ABCD
println(str) // abcd
- 문자열을 연결할 때는
+
사용 - 첫번째 요소가 문자열이면 다른 타입의 값과 연결 가능
val s = "abc" + 1
println(s + "def") // abc1def
문자열 리터럴
- 이스케이프 된 문자열: 이스케이프 문자열 포함
val s = "Hello, world!\n"
- 원시 문자열: 개행문자와 임의의 텍스트 포함
val text = """
for (c in "foo")
print(c)
"""
- 공백을 제거할 때는
trimMargin()
사용|
는 여백 접두사로 사용되지만 다른 문자열을 선택할 때는 trimMargin()에 매개변수로 전달 가능val text = """ |Tell me and I forget. |Teach me and I remember. |Involve me and I learn. |(Benjamin Franklin) """.trimMargin()
문자열 템플릿
- 평가되고 문자열에 연결된 코드 조각
$
로 시작
val i = 10
println("i = $i") // i = 10
- 중괄호에 표현식이 들어갈 수 있음
val s = "abc"
println("$s.length is ${s.length}") // abc.length is 3
- 원시 문자열과 이스케이프 문자열 모두 사용 가능
val price = """
${'$'}_9.99
"""
// $_9.99
배열
Array
클래스로 표현get
,set
함수를 가지며 연산자 오버로딩 컨벤션에 의해[]
로 변환됨size
속성 가짐
class Array<T> private constructor() {
val size: Int
operator fun get(index: Int): T
operator fun set(index: Int, value: T): Unit
operator fun iterator(): Iterator<T>
}
- 배열을 만들기 위해서
arrayOf()
함수 사용 arrayOfNulls()
는 주어진 크기에 null로 채워진 배열 생성- 배열 크기와 인덱스를 가진 배열 요소의 값을 반환하는 함수를 가지는
Array
생성자 사용 가능
// ["0", "1", "4", "9", "16"] 을 가지는 배열 생성
val asc = Array(5) { i -> {i * i}.toString() }
asc.forEach( println(it) )
- 코틀린 배열은 불변으로서
Array<String>
을Array<Any>
에 할당하는 것을 막으며 런타임 에러 방지
원시 타입 배열
원시 타입 배열
ByteArray
,ShortArray
,IntArray
등의 원시 타입 배열을 표현하는 클래스Array
클래스와 상속 관계가 없지만 같은 메소드와 속성을 가짐
val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]
// 크기는 5이며 [0, 0, 0, 0, 0]의 값을 가짐
val arr = IntArray(5)
// 크기는 5이며 [42, 42, 42, 42, 42]의 값을 가짐
val arr2 = IntArray(5) { 42 }
// 람다를 이용해 값을 초기화
val arr3 = IntArray(5) { it * 1 }