개발/코틀린

(코틀린) 코틀린 기초

DinoDev 2022. 9. 18. 17:45
728x90
반응형

기본 문법

주석

코틀린에서는 3가지 형태의 주석이 있습니다.

1줄 주석: //

여러줄 주석: /* */

KDoc 주석: /** */

// 1줄 주석

/*
여러줄
주석
*/

/**
KDoc
주석
*/

KDoc은 Rich Text Documentation을 생성하기 위해 사용합니다.

 

변수

코틀린에서 변수는 아래와 같이 정의합니다.

val name: String = "정석준" // String 타입의 name 변수에 "정석준" 문자열 할당

val name = "정석준" // 초기값이 있다면 타입 선언은 생략 가능

val name: String // 초기값이 없다면 에러 발생

val name = "정석준" // 불변변수
name = "dino" // val은 수정 할 수 없어서 에러 발생

var name = "정석준" // 가변변수
name = "dino" // var은 수정 할 수 있어서 에러 x

 

식별자(변수명)

코틀린에서 식별자의 규칙이 있습니다.

  • 문자, 숫자, _(underscore)만 포함가능하고 숫자로 시작할 수 없습니다.
  • 하드 키워드(val, fun 등)은 사용할 수 없습니다.
    • 다만 `(backqoute)를 사용하면 하드 키워드를 사용 할 수 있습니다.
  • `여기에는 공백도 가능 합니다.`
val _name1 = "정석준"
val `fun` = "함수"
val `class` = "클래스"
val `fun generate()` = "생성 함수"

 

연산자 우선순위

코틀린에서 값을 연산하는 연산자에는 우선순위가 있습니다.

후위 -> 전위 -> 사칙연산 -> 비교 -> 동등 -> 논리곱 -> 논리합 -> 대입

보통 사칙연산과 비교와 동등 빼고는 괄호를 넣어서 코드를 읽기 쉽게 하는게 좋다
/*
후위
++, --
 */
a*b++ // a*(b++)
++b-- // ++(b--)
a*b.foo() // a*(b.foo())

/*
전위
+ - ++ -- !
 */
+a * b // (+a) * b
++a * b // (++a) * b
!a || b // (!a) || b

/*
사칙연산
* / %
+ -
 */
 */
a * b + c // (a * b) + c
a - b % c // a - (b % c)

/*
중위(infix fun)
이름이 붙은 중위 연산자들
 */

a < b or b < c // (a < (b or b)) < c
a == b and b == c // (a == b) and (b == c)

/*
비교
< > <= >=
 */
a < b == b < c // (a < b) == (b < c)
a < b && b < c // (a < b) && (b < c)

/*
동등
== !=
 */
a == b || b != c // (a == b) || (b != c)

/*
논리곱
&&
 */
a || b && c // a || (b && c)

/*
논리합
||
 */

a && b || c // (a && b) || c

/*
대입
= += -= *= /= %=
 */
a = b * c // a = (b * c)
a *= a + b // a *= (a + b)

타입

int, boolean과 같은 primitive type(원시 타입의 값은 메서드의 스택 영역에 저장될 수 있다)과 String과 같은 reference type(참조 타입의 값은 스택 영역에 저장될 수 있다)이 있습니다.

 

정수

정수를 표현하는 타입은 4가지가 있고 아래와 같이 구성되어있습니다.

이름 크기(바이트) 범위
Byte 1 -128 .. 127
Short 2 -32768 .. 32767
Int 4 -2^31 .. 2^31-1
Long 8 -2^63 .. 2^63-1
/*
숫자 표현 방식
정수를 표현 할 때는 숫자만 구성해도 되고 숫자 사이에 _(underscore)를 사용해서 가독성을 높일 수 있음
 */
val n = 12345
val n2 = 12_345_789

/*
크기에 따른 변수 타입
 */
val one: Byte = 1
val tooBigForShort: Short = 100_000 // The integer literal does not conform to the expected type Short
val million = 1_000_000 // Int 타입 추론
val tooBigForInt: Int = 10_000_000_000 // The integer literal does not conform to the expected type Int
val tenBillions = 10_000_000_000 // Long 타입 추론
val tooBigForLong = 10_000_000_000_000_000_000 // The value is out of range

/*
L이나 l을 접두사로 붙여서 Long타입 강제
 */
val hundredLong = 100L
val hundredInt: Int = 100L // The integer literal does not conform to the expected type Int

/*
접미사로 0b(2진수) 0x(16진수)를 표현
 */
val bin = 0b10101 // 21
val hex = 0xf9 // 249

/*
기타
- 다른 언어는 0으로 시작하면 8진수를 표현하지만 코틀린에서는 지원하지 않음
- 음수를 표현하는 - 표현은 리터럴이 아닌 음수연산자(-)를 사용한 식
 */
val zero = 0
val zeroOne = 01 // Property getter or setter expected, Unsupported [literal prefixes and suffixes]

val neg = -10
val negHex = -0xff

 

부동소수점

IEEE 754 부동소수점 수를 따르는 Float과 Double 타입이 있습니다.

/*
부동 소수점을 나타낼 때는 숫자 사이에 .을 넣어서 표현
 */
val pi = 3.14
val one = 1.0

/*
0 미만의 수인 경우 0을 생략할 수 있고 .뒤에 숫자가 비어있을 수는 없음
 */
val quarter = .25
val one = 1. // Expecting an element
val two = 2

/*
과학적 표기법(scientific notation) 리터럴을 허용
 */
val pi = 0.314E1 // 3.14, 0.314*10
val pi100 = 0.314E3 // 314.0, 0.314*1000
val piOver100 = 3.14E-2 // 0.0314, 3.14/100
val thousand = 1E3 // 1000.0, 1*1000

/*
부동 소수점은 기본 Double 타입이고 Float 타입으로 사용하고 싶으면 접미사로 f, F를 붙임
 */
val pi = 3.14f
val one = 1f
val two: Double = 2f // The floating-point literal does not conform to the expected type Double

/*
특별한 값을 표현하는 몇 가지 상수
 */
Float.MIN_VALUE // 1.4E-45
Double.MAX_VALUE // 1.7976931348623157E308
Double.POSITIVE_INFINITY // Infinity
1.0/Double.NEGATIVE_INFINITY // -0.0
2 - Double.POSITIVE_INFINITY // -Infinity
3 * Float.NaN // NaN <- NaN은 잘못된 입력으로 인해 계산을 할 수 없음을 나타내는 기호

 

산술 연산

+ 덧셈

- 뺄셈

* 곱셈

/ 나눈 몫

% 나눈 나머지

 

floorDiv()몫을 더 작은 정수로 내림한다.

mod()는 a와 a.floorDiv(b) * b의 차이를 반환한다. mod()의 결과값의 부호는 b와 항상 같다.

7.floorDiv(4) // 1
(-7).floorDiv(4) // -2
7.floorDiv(-4) // -2
(-7).floorDiv(-4) // 1
7.mod(4) // 3
(-7).mod(4) // 1
7.mod(-4) // -1
(-7).mod(-4) // -3

단항 +, - 연산의 결과는 인자들의 타입과 같고 Byte와 Short인 경우는 Int를 반환합니다.

val byte: Byte = 1
val int = 1
val long = 1L
val float = 1.5f
val double = 1.5
-byte // -1: Int
-int // -1: Int
-long // -1: Long
-float // -1.5: Float
-double // -1.5: Double

다른 타입끼리 이항 산술 연산을 하면 크기가 더 큰 타입으로 변경되며 Double > Float > Long > Int > Short > Byte 순으로 변경됩니다.

val byte: Byte = 1
val int = 1
val long = 1L
val float = 1.5f
val double = 1.5
byte + byte // Byte
int + byte // Int
int + int // Int
int + long // Long
long + double // Double
float + double // Double
float + int // Float
long + double // Double

 

비트 연산

정수 타입은 비트 연산을 지원합니다.

연산 예제 결과
shl 왼쪽 시프트 13 shl 2
(-13) shl 2
52: 0...0011_0100
-52: 1...1100_1100
shr 오른쪽 시프트 13 shr 2
(-13) shr 2
3: 0...0000_0011
-4: 1...1111_1100
ushr 부호 없는 오른쪽 시프트 13 ushr 2
(-13) ushr 2
3: 0...0000_0011
1073741820: 001...1111_1100
and 비트 곱(AND) 13 and 19
-13 and 19
1: 0...0000_0001
19: 0...0001_0011
or 비트 합(OR) 13 or 19
-13 or 19
31: 0...0001_1111
-13: 1...1111_0011
xor 비트 배타합(XOR) 13 xor 19
-13 xor 19
30: 0...0001_1110
-32: 1...1110_0000
inv 비트 반전(inversion) 13.inv()
(-13).inv()
-14: 1...1111_0010
12: 0...0000_1100

 

문자 타입 Char

유티코드 한 글자를 표현하며 16비트 입니다. `(backquote) 사이에 문자를 넣으면 됩니다.

val z = 'z'
val one = '1'

특수 문자를 위해 이스케이프를 제공합니다.

val tab = '\t'
val backspace = '\b'
val newline = '\n'
val carriageReturn = '\r'
val singleQuote = '\''
val doubleQuote = '\"'
val backslash = '\\'
val dollarSign = '\$'

\u 다음 16진수 4자리를 넣어서 유니코드 문자를 만들 수 있습니다.

val pi = '\u03c0'

Char 타입은 덧셈과 뺄셈을 지원하고 ++, -- 로 1씩 증가와 감소를 지원합니다.

var a = 'a'
var h = 'h'
a + 5 // f
a - 5 // \
h - a // 7
--h // g
++a // b

 

수 변환

각 수 타입마다 다른 수 타입으로 변환하는 함수가 정의되어 있습니다.

toByte(), toShort(), toInt(), toLong(), toFloat(), toDouble(), toChar()

 

정수 타입 사이의 변환은 대상 타입이 작은 경우 MSB(2진수로 표현했을 때 상위 비트쪽)를 잘라내고 나머지를 대상 타입의 값으로 변환 합니다.

val n = 945
n.toByte() // -79
n.toShort() // 945
n.toChar() // α
n.toLong() // 945

부동소수점 수 타입과 같은 경우 정밀도를 잃을 수 있다.

Long을 Float로 변환하면 LSB(2진수로 표현했을 때 하위 비트 쪽)을 잃어 버립니다.

2.5.toInt() // 2
(-2.5).toInt() // -2
1_000_000_000_000.toFloat().toLong() // 999999995904

 

불 타입과 논리 연산

참(true)과 거짓(false)로 표현하는 boolean 타입이 있습니다.

논리연산은 !, or, and, xor, ||, &&가 있습니다.

 

지연 계산 연산자인 ||, &&는 상황에 따라 연산을 수행하지 않을 수 있습니다

||는 왼쪽 피연산자가 true면 오른쪽 피연산자를 계산하지 않는다.

&&는 왼쪽 피연산자가 false면 오른쪽 피연산자를 계산하지 않는다.

 

비교와 동등성

==(같다), !=(같지 않다), <(작다), >(크다), <=(작거나 같다), >=(크거나 같다)

val a = 1
val b = 2
a == 1 || b != 1 // true
a >= 1 && b < 3 // true
a < 1 || b < 1 // false
a > b // false

==와 !=는 두 인자가 모두 같은 타입일 때만 허용합니다.

val a = 1
val b = 2L
a == b // Operator '==' cannot be applied to 'Int' and 'Long'
a.toLong() == b // false

<, >, <=, >=는 같은 유형의 타입 끼리 지원 합니다.

1 <= 2L || 3 > 4.5 // true
false == true // false
false < true // true
false > 1 // The integer literal does not conform to the expected type Boolean
'a' < 'b' // true
'a' > 0 // The integer literal does not conform to the expected type Char

NaN 값은 어떤 값과도 같지 않고 크거나 작지도 않다.

NaN은 실제로 특정 값으로 인코딩 되어 있어서 NaN값인지 확인하려면 == 보다 .isNaN() 함수를 사용해야만 합니다.

Double.NaN == Double.NaN // false
Double.NaN != Double.NaN // true
Double.NaN <= Double.NaN // false
Double.NaN < Double.POSITIVE_INFINITY // false
Double.NaN > Double.NEGATIVE_INFINITY // false
Double.NaN.isNaN() // true

public static boolean isNaN(double v) {
    return (v != v);
}

NaN은 자기 자신과 같고 가장 큰 값으로 표현됩니다.

val set = sortedSetOf(Double.NaN, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, 0.0)
println(set) // [-Infinity, 0.0, Infinity, NaN]

문자열

문자열 템플릿

String 타입의 문자열을 만들 때 "를 사용하는데 "와 " 사이에 변수나 식을 넣을 때는 $나 ${}를 사용합니다.

간단한 참조인 경우 $를 사용하고 식을 사용할 땐 ${}를 사용합니다.

fun main() {
    val name = "정석준"
    val age = 31
    println("안녕하세요. 제 이름은 ${name}이고 나이는 ${age}살 입니다.")
    // 안녕하세요. 제 이름은 정석준이고 나이는 31살 입니다.
}

"를 3개 사용하면 여러줄을 사용한 문자열을 만들 수 있습니다. trimIndent()를 이용해서 indent를 제거 해줍니다.

fun main() {
    val name = "정석준"
    val age = 31
    val introduction = """
        안녕하세요. 제 이름은 ${name}이고 나이는 ${age}살 입니다.
        안드로이드 개발 6년차이고 토스를 다니고 있습니다.
    """.trimIndent()
    println(introduction)
    /*
    안녕하세요. 제 이름은 정석준이고 나이는 31살 입니다.
    안드로이드 개발 6년차이고 토스를 다니고 있습니다.
     */
}

 

기본 문자열 연산

// length나 lastIndex 처럼 숫자를 반환하는 property가 있음
"Hello!".length.print() // 6
"hello!".lastIndex.print() // 5

// [index]를 사용해서 개별 문자를 가져 올 수 있음
val s = "Hello!"
s[0].print() // H
s[1].print() // e
s[5].print() // !
s[10].print() // Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 10

// + 연산자를 사용해서 문자열을 연결 할 수 있음
val s1 = "Hello!"
val s2 = "Hel" + "lo!"
// == 를 통해서 문자열 비교를 할 수 있음
s1 == s2 // true
// 문자열은 사전식 순서로 정렬되서 > < >= <= 와 같은 비교 연산도 가능
"abc" < "cba" // true
"123" > "34" // false

/*
문자열을 다른 타입으로 변환하는 toByte(), toShort(), toInt(), toLong(), toFloat(), toDouble(), toBoolean() 함수를 제공
그 외에 유용한 함수들도 많이 있음
 */
"".isEmpty() // true
"".isNotEmpty() // false
"Hello".substring(2) // llo
"Hello".substring(1, 3) // el
"Hello".startsWith("Hel") // true
"Hello".endsWith("Hel") // false
"Hello".indexOf("e") // 1
"Hello".indexOf("a") // -1
"Hello".indexOf("l", 2) // 3

배열

코틀린에 내장된 데이터 구조로 크기가 정해져 있고 인덱스로 참조 할 수 있습니다.

배열 정의하기

val a = emptyArray<String>() // Array<String> 원소 0개
val b = arrayOf("Hello", "world") // Array<String> 원소 2개
val c = arrayOf(1, 4, 9) // Array<Int> 원소 3개

val squares = Array(10) { (it + 1) * (it + 1) } // size와 init block을 지정해서 초기화 가능, init block은 0부터 size-1까지 전달

 

배열 사용하기

배열 타입은 문자열 타입 처럼 [index]로 접근이 가능하고 원소를 변경 할 수도 있습니다.

val squares = arrayOf(1, 4, 9, 16)
squares.size // 4
squares.lastIndex // 3
squares[3] // 16
squares[1] // 4

squares[2] = 100 // 1, 4, 100, 16
squares[3] += 9 // 1, 4, 100, 25
squares[0]-- // 0, 4, 100, 25

val numbers = squares
numbers[0] = 1000
squares[0] // 1000 <- squares와 numbers는 같은 배열을 참조

val numbers2 = squares.copyOf()
numbers2[0] = 1234 // squares는 변화 없음
squares.copyOf(2) // 0, 4
squares.copyOf(5) // 0, 4, 100, 25, 0

var a = arrayOf(1, 2, 3, 4)
a = arrayOf("one", "two") // Array<Int>에 Array<String>으로 변환 할 수 없음

// + 연산자를 통해서 원소를 추가 할 수 있음
intArrayOf(1, 2, 3) + 4 // 1, 2, 3, 4
intArrayOf(1, 2, 3) + intArrayOf(5, 6) // 1, 2, 3, 5, 6

// 배열의 원소 비교는 ==가 아니라 contentEquals로 비교
intArrayOf(1, 2, 3) == intArrayOf(1, 2, 3) // false
intArrayOf(1, 2, 3).contentEquals(intArrayOf(1, 2, 3)) // true

// isEmpty, isNotEmpty, indexOf와 같은 함수도 제공
intArrayOf(1, 2).isEmpty() // false
intArrayOf(1, 2).isNotEmpty() // true
intArrayOf(1, 2).indexOf(2) // 1
intArrayOf(1, 2).indexOf(4) // -1
728x90
반응형