0. 오리엔테이션 1. 코틀린의 기본의 기본을 읽혀요! - 코틀린이란 무엇일까? - 변수와 자료형, 연산자 2. 함수형 프로그래밍 이란? - 마법의 요술상자, 함수의 기본 - 요술상자, 함수 가지고 놀기 3. 프로그램 흐름의 제어 - 프로그램의 흐름을 제어해보자! 4. 코틀린의 표준함수 활용하기 - 코틀린과 표준함수 5. 강좌 마무리 프로젝트 |
▶ 깃허브 코드 : github.com/0525hhgus/Kotlin-study
[ 변수와 자료형, 연산자 ]
1. 널(Null)
1) 널을 허용하는 변수 선언하기
▶ 코틀린의 변수 선언은 기본적으로 null을 허용하지 않는다.
val a: Int = 30
var b: String = "Hello"
▶ null이 가능한 선언
- 선언 자료형의 접두사로 ? 추가
val a: Int? = null
var b: String? = null
▶ NPE (NPE, NullPointerException)
- 단순 출력은 상관 없으나 널인 상태에서 연산되는 멤버에 접근할 때
- 사용할 수 없는 null인 변수에 접근하면서 발생하는 예외
→ 안드로이드 프로그래밍할 때, 앱이 실행 중에 꺼지면 대부분 이 오류였습니다.
2) 실습 : 기본적인 널 가능한 선언 변수의 예
▶ 초기화 오류 예제
package chap02.section3
fun main(){
val str1: String
println(str1)
}
▶ 기본 선언은 널 불가능 → 널 가능한 형식은 [데이터형?]로 선언
package chap02.section3
fun main(){
val str1: String?
str1 = null
println(str1)
}
3) 실습 : 세이프콜과 넌널 단정기호
▶ 세이프콜 예제
package chap02.section3
fun main(){
val str1: String?
str1 = null
println("str: $str1, length: ${str1?.length}")
}
- str1?.length : str1이 null이면 length(뒷부분) 실행 X
▶ 넌널 단정 기호 예제
- 넌널 단정 기호 !!.는 널이 아닐 것이라 단정하여 컴파일러가 오류를 무시하도록 함
package chap02.section3
fun main(){
val str1: String?
str1 ="Kotlin!"
println("str: $str1, length: ${str1!!.length}")
}
package chap02.section3
fun main(){
val str1: String?
str1 = null
println("str: $str1, length: ${str1!!.length}")
}
→ NPE (NPE, NullPointerException) 발생!
4) 실습 : 판단문과 엘비스 연산자를 활용한 검사
▶ 판단문
package chap02.section3
fun main(){
val str1: String?
str1 = null
val len = if(str1 != null) str1.length else -1;
println("str: $str1, length: ${len}")
}
- 코틀린은 if 판단문을 한줄에 구성 가능
▶ 엘비스(elvis) 표현식
package chap02.section3
fun main(){
val str1: String?
str1 = null
val len = str1?.length ?: -1
println("str: $str1, length: ${len}")
}
→ 다른 언어(C, Python 등)에서의 삼항 연산자와 형태가 비슷하여 추가로 조사했으나,
kotlin은 if만으로도 3항 연산자와 동일한 역할의 처리가 충분히 가능하기 때문에, 별도의 3항연산자를 채택하지 않았다고 합니다.
출처: http://kotlinlang.org/docs/reference/control-flow.html#if-expression
5) 요약
▶ Kotlin의 처리방법 비교
- Kotlin에서는 기본적으로 NotNull이고 Nullable 표현에만 '?'가 사용된다.
fun set(a: String, b: String?){
// Do notting
}
var temp: String? = null
var size = -1
if (temp != null) {
size = temp.length
}
// or
var temp: String? = null
val size = if (temp != null) temp.length else -1
▶ 세이프 콜(Safe-call)
- str1?.length
▶ non-null 단정 기호
- str1!!.length
▶ if와 else의 활용
fun main(){
val str1: String? = "Hello Kotlin!"
str1 = null
// 조건식을 통해 null 상태 검사
val len = if(str1 != null) str1.length else -1;
println("str: $str1, length: ${len}")
}
▶ 더 안전하게 사용하는 방법
- str1?.length ?: -1
fun main(){
val str1: String? = "Hello Kotlin!"
str1 = null
// 조건식을 통해 null 상태 검사
val len = str1?.length ?: -1
println("str: $str1, length: ${len}")
}
🤔 생각해보세요.
널을 최대한 사용하지 않기 위해 코틀린은 기본적인 선언 모두 반드시 초기화 해야하는 넌-널 타입입니다. 왜 널을 최대한 피해야 할까요? 그리고 val선언처럼 변경 불가능한(immutable) 변수를 적극 활용하는 이유는 무엇일까요? 프로그램을 하다가 널인 상황과 NPE를 만났던 상황과 해결했던 경험을 댓글로 공유해보면서 위와 같은 문제를 생각해봅시다.
Q. 왜 널을 최대한 피해야 할까요?
A. NPE(NullPointerException)를 발생시킬 수 있기 때문입니다.
Q. 변경 불가능한(immutable) 변수를 적극 활용하는 이유는 무엇일까요?
A. 변수가 변경되어 버그를 발생시키는 상황을 예방하기 위해서 입니다. 자세한 사항은 링크로 확인할 수 있습니다.
kotlinlang.org/docs/reference/native/immutability.html
Q. 프로그램을 하다가 널인 상황과 NPE를 만났던 상황과 해결했던 경험
A. 자바로 안드로이드 앱을 개발하던 중 화면(Activity, Fragment) 간 데이터를 전달 받는 과정에서 NPE 오류와 함께 앱이 강제 종료되는 상황이 자주 있었습니다. NPE 오류를 발생시키는 변수를 찾아 적절하게 초기화하는 방법으로 해결했습니다. 추가로 대부분 아실 것 같은데 스택 오버플로우 사이트(stackoverflow.com/)를 적극 활용하였습니다.
2. 검사와 자료형 변환
1) 코틀린의 자료형 변환 함수
▶ 코틀린의 자료형 변환
- 기본형을 사용하지 않고 참조형만 사용
- 서로 다른 자료형은 변환 과정을 거친 후 비교
val a: Int = 1 // 정수형 변수 a를 선언하고 1을 할당
val b: Double = a // 자료형 불일치 오류 발생
val c: Int = 1.1 // 자료형 불일치 오류 발생
- 변환 메서드의 이용
val b: Double = a.toDouble() // 변환 메서드 사용
- 표현식에서 자료형의 자동 변환
val result = 1L + 3 // Long + Int -> result는 Long
→ 큰 자료형으로 자동 변환
▶ 변환 메서드의 종류
변환 메서드 |
toByte: Byte |
toLong: Long |
toShort: Short |
toFloat: Float |
toInt: Int |
toDouble: Double |
toChar: Char |
2) 이중 등호와 삼중 등호
▶ 이중 등호(==)와 삼중 등호(===)의 사용
- == : 값만 비교하는 경우 → 자바에서는 참조 주소까지 비교
- === : 값과 참조 주소를 비교하는 경우
val a: Int = 128
val b: Int = 128
println(a == b) // true
println(a === b) // true
▶ 참조 주소가 달라지는 경우
val a: Int = 128
val b: Int? = 128
println(a == b) // true
println(a === b) // false
3) 실습 : 이중 등호와 삼중 등호
package chap02.section3
fun main() {
val a: Int = 128
val b = a
println(a == b)
println(a === b)
val c: Int? = a
val d: Int? = a
val e: Int? = c
println(c == d)
println(c === d) // 주소 공간이 다름
println(c === e) // 주소 공간이 같음
}
4) 기본형 값과 참조형 값의 설명
★ 코틀린에서는 참조형으로 선언한 변수의 값이 -128~127 범위에 있으면 캐시에 그 값을 저장함
5) 스마트 캐스트
▶ 구체적으로 명시되지 않은 자료형을 자동 변환
- 값에 따라 자료형을 결정
- Number형은 숫자를 저장하기 위한 특수한 자료형으로 스마트 캐스됨
▶ 스마트 캐스트 사용해보기
fun main() {
var test: Number = 12.2 // 12.2에 의해 test는 Float형으로 스마트 캐스트
println("$test")
test = 12 // Int형으로 스마트 캐스트
println("$test")
test = 120L // Long형으로 스마트 캐스트
println("$test")
test += 12.0f // Float형으로 스마트 캐스트
println("$test")
}
→ 문자열로는 캐스트 불가능
6) is를 사용한 자료형 검사
▶ is 키워드를 사용한 검사
fun main() {
var num = 256
if (num is Int) { // num이 Int형일 때
print(num)
} else if (num !is Int) { // num이 Int형이 아닐 때, !(num is Int)와 동일
print("Not a Int")
}
}
7) Any를 통한 묵시적 변환
▶ Any
- 자료형이 정해지지 않은 경우
- 모든 클래스의 뿌리 : Int나 String은 Any형의 자식 클래스이다.
- Any는 언제든 필요한 자료형으로 자동 변환 (스마트 캐스트)
▶ Any형 변수의 변환
package chap02.section3
fun main() {
var a: Any = 1 // Any형 a는 1로 초기화될 때 Int형이 됨
a = 20L // Int형이었던 a는 변경된 값 20L에 의해 Long이 됨
println("a: $a type: ${a.javaClass}") // a의 자바 기본형을 출력하면 long이 나옴
}
▶ Any형으로 인자를 받는 함수 만들기
package chap02.section3
fun main() {
checkArg("Hello") // 문자열을 인자로 넣음
checkArg(5) // 숫자를 인자로 넣음
}
fun checkArg(x: Any) { // 인자를 Any형으로 받음
if (x is String) {
println("x is String: $x")
}
if (x is Int) {
println("x is Int: $x")
}
}
🤔 생각해보세요.
스택에 들어간 값과 힙에 들어간 값에 대해 좀 더 생각해봅시다. 어떤 변수들이 스택(Stack)에 들어가며 어떤 변수들은 힙(Heap)에 들어갈까요?
Q. 어떤 변수들이 스택(Stack)에 들어가며 어떤 변수들은 힙(Heap)에 들어갈까요?
A. Stack은 지역 변수와 정적 변수 등이 호출 순서에 따라 메모리에 차례로 저장하는 역할을 수행하고, Heap은 필요에 따라 필드나 메소드등을 포함한 인스턴스화가 된 객체 변수에 동적으로 메모리를 할당하는 역할을 수행합니다.
machine code |
- 메모리 영역 (CS50 수업 참고) (1) machine code(코드) 영역 (2) globals(데이터) 영역 (3) stack 영역 (4) heap 영역 |
||||
globals | |||||
heap ↓ ↑ stack |
→ 제 설명이 많이 부족한 것 같아 Java와 Kotlin의 메모리 영역을 다룬 자료를 추가합니다.
medium.com/@cocoa3078/kotlin과-java의-가장-바닥-d51db10b87b7
3. 연산자를 조합해 다양한 식 만들기 (1) 기본 연산자
1) 연산자
▶ 종류
- 산술, 대입, 증가, 감소, 비교, 논리 연산자 등
▶ 수식의 구조
val result = num1 + num2
- = : 대입 이항 연산자
- num1 + num2 : 표현식
- num1, num2 : 항
2) 산술 연산자
▶ 사칙연산에 사용되는 사칙 연산자(+, -, *, /)와 나머지 연산자(%)
3) 대입 연산자
▶ 변수에 값을 할당하는 연산자(=, +=, -=, *=, /=, %=)
num = num + 2 // 산술 연산자와 대입 연산자를 함께 사용하는 경우
num += 2 // 이렇게 간략하게 표현
4) 증감 연산자
▶ 증가 연산자(++)와 감소 연산자(--)는 항이 1개인 단항 연산자
fun main() {
var num1 = 10
var num2 = 10
var result1 = ++num1 // num1 값 증가 후 대입
var result2 = num2++ // 먼저 num2 값 대입 후 증가
println("result1: $result1")
println("result2: $result2")
println("num1: $num1")
println("num2: $num2")
}
5) 실습 증감 연산자 위치에 따른 할당 결과
package chap02.section3
fun main() {
var a = 10
var b = 10
var result1 = ++a // num1 값 증가 후 대입
var result2 = b++ // 먼저 num2 값 대입 후 증가
println("result1: $result1")
println("result2: $result2")
println("a: $a")
println("num2: $b")
}
6) 비교 연산자
▶ 두 개의 항을 비교하기 위해 사용 (>, <, >=, <=, ==, !=, ===, !==)
▶ 비교 결과가 참이면 true, 거짓이면 false 값 반환
7) 논리 연산자
연산자 | 의미 |
&& (논리곱) | 2개의 항이 모두 true인 경우에만 true를 반환 |
|| (논리합) | 2개의 항 중에 1개의 항만 true이면 true를 반환 |
! (부정) | 단항 연산자로 true과 false의 값을 반대로 바꿈 |
🤔 생각해보세요.
%(모듈로) 연산자에 대해서 생각해봅시다. 짝수와 홀수를 구하는 것 이외에 어떤것에도 활용할 수 있을까요?
Q. %(모듈로) 연산자가 어떤것에도 활용할 수 있을까요?
A. 해시 테이블의 해시 함수 또는 원형 큐를 구현하는데 있어 반복적으로 배열에 접근하기 위해 사용될 수 있습니다.
4. 연산자를 조합해 다양한 식 만들기 (2) 비트 연산자
1) 비트연산의 이해
▶ 비트와 비트 연산 이해하기
- 1010(2진수) = 2^1 + 2^3 = 10(10진수)
- 가장 왼쪽에 있는 비트는 양(+), 음(-)을 판단하는데 사용
2) 비트 연산자의 종류
▶ 비트 연산을 위한 비트 메서드
표현식 |
설명 |
4.shl(bits) |
4를 표현하는 비트를 bits만큼 왼쪽으로 이동(부호 있음) |
7.shr(bits) |
7을 표현하는 비트를 bits만큼 오른쪽으로 이동(부호 있음) |
12.ushr(bits) |
12를 표현하는 비트를 bits만큼 오른쪽 이동(부호 없음) |
9.and(bits) |
9를 표현하는 비트와 bits를 표현하는 비트로 논리곱 연산 |
4.or(bits) |
4를 표현하는 비트와 bits를 표현하는 비트로 논리합 연산 |
24.xor(bits) |
23를 표현하는 비트와 bits를 표현하는 비트의 배타적 연산 |
78.inv |
78을 표현하는 비트를 모두 뒤집음 |
3) 실습 : 비트 연산자 사용하기
package chap02.section3
fun main() {
val x = 4 // 0100(2진수), 4(10진수)
val y = 0b0000_1010 // 5(10진수)
val z = 0x0f // 0b0000_1111(2진수), 15(10진수)
println("x sh1 2 -> ${x.shl(2)}") // 16(10진수). 0001_0000(2진수)
println("x inv -> ${x.inv()}") // -5(10진수), 111....1011(2진수)
}
감사합니다!
'대외활동 > Naver Boostcourse' 카테고리의 다른 글
[부스트코스] 코틀린 프로그래밍 기본 1/2(함수편) - 람다식과 고차 함수 (0) | 2021.02.01 |
---|---|
[부스트코스] 코틀린 프로그래밍 기본 1/2(함수편) - 함수의 기본, 함수형 프로그래밍 (0) | 2021.01.26 |
[부스트코스] 코틀린 프로그래밍 기본 1/2(함수편) - 변수와 자료형 (0) | 2021.01.12 |
[부스트코스] 코틀린 프로그래밍 기본 1/2(함수편) - 코틀린 소개 (0) | 2021.01.11 |
[부스트코스] 코틀린 프로그래밍 기본 1/2(함수편) - OT, 코틀린이란? (0) | 2021.01.11 |