Do it 코틀린 프로그래밍 책을 바탕으로 Kotlin에 대해 스터디를 진행합니다.
정적언어와 동적언어?
1. 정적언어 : 자료형을 컴파일시에 결정하는것 (ex c,c++,java)
컴파일시에 결정하기 때문에 속도가 빠르다. 타입에러 문제를 초기에 발견할 수 있다.
2. 동적언어 : 실행시에 자료형을 결정. 자료형 없이 변수만 선언하여 값을 지정할 수 있음(ex python js)
런타임에 타입을 결정하기 때문에 선택의 여지가 많음. 하지만 실행 도중 변수에 예상치 못한 타입이 들어와 타입에러가 생길 수 있음
코틀린은 널포인터가 일어날 수 있는 상황에서는 컴파일이 되지않는다
var name: String ="abc"
name=null //컴파일 에러 발생
val : 최초로 지정한 변수의 값으로 초기화 하고 더이상 바꿀 수 없는 읽기전용 변수가 됨
var : 반면 var은 바꿀 수 있다.
```
ex)
val username: String ="Kildong"
```
"Kildong"을 보고 string이라고 추론한다.
ex)
val username="Kildong"
자료형을 지정하지 않은 변수는 추론할 값을 지정해야한다.
참조형과 기본형?
기본형으로 선언한 변수는 주로 임시 메모리인 스택에 저장되며 메모리 크기도 고정되어있다.
참조형은 스택에 값이 아닌 참조 주소가 있다. 참조주소를 따라가면 힙에 저장된 실제 객체가 있다. 그래서 기본형이 수행시간이 더 빠르다.
기본형(Primitive)은 int long float double
참조형(Reference)는 String Date
참조형으로 선언된 변수는 성능 최적화를 위해 코틀린 컴파일러에서 다시 기본형으로 대체된다. => 참조형을 서서 느릴것같지만 컴파일과정에서 기본형으로 바귀기 때문에 최적화 고려할 필요 X
같은 문자열이 저장되어 있는 경우 힙영역의 String pool에 해당 문자열을 저장해두고 변수가 참조하도록 만든다. ===연산자를 사용하여 참조비교하면 true가 반환된다.
var a=1
val s1="a is $a"
표현식을 문자열에 포함하려면
val str="a=${a+2}"
이런식으로 사용한다.
typealias Username=String //String을 Username이라는 별명으로 대체
- 변수에 null을 할당하려면 자료형 뒤에 ?를 붙여줘야함
```
var str: String?="Hello Kotlin"
str=null
```
- 세이프 콜 : null이 할당되어 있을 가능성이 있는 변수를 검사하여 안전하게 호출하도록 도와주는 기법. 변수 이름 뒤에 ?. 명시
```
println("str: $str leng: ${str?.length}
//null이라면 length에 접근하지 않고 null 출력
```
- non-null 단정기호 : 변수에 할당된 값이 null이 아니라고 단정하여 컴파일러가 null검사 하지않게함 (null있으면 실행중에 널포인터뜸) 변수이름 뒤에 !!. 명시
- 엘비스 연산자 : 변수가 null인지 검사하여 아니라면 왼쪽식을 null이라면 오른쪽식을 실행함
```
println("length : ${str?.length?:-1}")
//null이 아니면 length가, null이면 -1이 출력됨
```
자료형 변환 메서드를 사용해야함 (ex toDouble)
연산 시에는 범위가 큰 자료형으로 자동형 변환하여 연산한다.
Number 형과 같은 스마트 캐스트가 적용되는 자료형이 있다.
자료형 검사를 위해 is를 사용한다.
if(num is Int){
return true;
}
//비트연산자 생략
fun sum(a:Int,b:Int):Int{ //함수 키워드 : fun 함수명 매개변수 러턴형
var sum=a+b
return sum
}
간략하게
fun sum(a:Int,b:Int):Int=a+b
or
fun sum(a:Int,b:Int):a+b
이렇게 생략할 수 있음
return 문을 생략할 수 있다. 반환 자료형을 Unit으로 지정하거나 생략하면 된다.
단 생략해도 반환값이 Unit으로 추론된다. (void와 기능이 같지만
fun printSum(a:Int,b:Int):Unit{
print("sum of $a and $b is ${a+b}")
}
fun add(name: String, email: String="default"){
}
fun add(vararg counts: String){
for(num in counts){
print("$num")
}
}
fun main() {
add(1,2,3,4)
add(4,5,6)
//이렇게 둘 다 가능
}
vararg를 매개변수 앞에 붙인다. String 형 배열로 전달된다.
함수의 정보는 프레임이라는 정보로 스택의 높은 주소부터 채워진다
지역변수는 함수가 종료되면 스택 프레임과 함께 사라진다.
힙영역에는 동적으로 생성된 객체의 정보가 담겨있는데 힙에서는 낮은 주소에서 높은 주소로 정보를 저장한다. 그래서 두영역이 만나지 않도록 메모리를 관리하는것이 중요하다 무한 함수호출로 스택 프레임이 정해진 스택 영역 경계를 넘어가면 스택 오버플로우가 발생한다.
다른 함수를 인자로 사용하거나(or) 함수를 결괏값으로 반환하는 함수
=> 일급 객체 혹은 일급 함수를 서로 주고받을 수 있는 함수가 고차함수이다.
fun main(){
val res1=sum(3,2) //일반인자
val res2=mul(sum(3,3),3) //인자에 함수를 사용
println("funcFunc:${funcFunc()}")
}
fun sum(a:Int, b:Int)=a+b
fun mul(a:Int, b:Int)=a*b
fun funcFunc():Int{ //함수의 반환값으로 함수 사용
return sum(2,2)
}
fun main(){
var result:Int
val multi={x:Int,y:Int->x*y} //일반 변수에 람다식 할당
result=multi(10,20) //람다식이 할당된 변수는 함수처럼 사용 가능
println(result)
}
궁금한점 : 함수랑 뭐가다른지 ??????
val multi : (Int,Int)-> Int={x:Int,y:Int-> x*y} //생략되지 않은 전체 표현
val multi :{x:Int, y:Int->x*y} //선언 자료형 생략 매개변수에 자료형이 지정되어 있어 추론 가능함
val multi:(Int,Int)->Int={x,y-> x*y}
// 람다식 매개변수 자료형의 생략
// 람다식 매개변수, 선언 자료형 둘다 생략하면 추론이 불가능 하므로 오류가 발생한다.
val greet : ()-> Unit={println("Hello World"} //매개변수 없음 반환값 없음(Unit)
val square: (Int)->Int={x->x*x} //매개변수가 하나만 있을 때 람다식의 자료형 생략한 것
123 람다식과 고차함수 호출하기
그림 출처 : Do it! 코틀린 프로그래밍
정적언어와 동적언어
코틀린의 null처리