어셈블리어 : 0,1로 사용하는 최하위 언어, 사람이 이해하기 어렵다
C언어 : 절차지향, 하드웨어를 다루는데 인간친화적 -> 임베디드 분야에 많이 사용
Java : 객체지향, 대형 소프트웨어 개발에 적합 -> 스마트폰의 앱들도 JVM 위에서 도는 Java 앱들이다.
Kotlin : 풀스택 웹 개발, Andorid, IOS 앱, 임베디드, IoT등 모든 개발을 다양한 플랫폼에서 개발할 수 있는 언어
최초 목적은 Andorid 앱에서 Java를 대체하기 위함
IDE(통합개발환경)로 유명한 JetBrains에서 개발하고 보급
간결하고 다재다능한 코드, 호환성 높다
Nullable과 Non-Nullable 변수 구분
문장 끝의 ; 생략가능
Andorid Studio에서 Android 공식 언어로 추가되었다.
Kotlin/JVM : JVM 상에서 동작하는 앱을 만들 수 있다. -> 즉 안드로이드에서 돌아가는 앱을 만들 수 있다. (안드로이드는 중간단계에서 JVM을 사용함)
언어 문법은 Kotlin, JS가 동일하다. 따라서 하나의 코드로 다양한 플랫폼에서 사용가능하다.
Kotlin/JS : JS에 의해 브라우저에서 웹 앱으로 작동하게 할 수 있다.
Kotlin/Native : Native는 특정 기기에 맞춰진 코드를 만들어낸다. LLVM이라는 특정 중간 코드가 다양한 기기에서 코드를 실행할 수 있게 해준다. -> 멀티플랫폼용 코드
현재는 Kotlin/JS와 Kotlin/Native는 내용이 많지 않다.
먼저 Kotlin/JVM에 대해 배운다
JDK(Java Development Kit)가 필요하다. Kotlin을 JVM 상에서 동작하게 하기 위함
기존 자바와 상호작용하고 자바 라이브러리를 이용할 수 있다.
Oracle JDK : 라이센스 구독 필요
Open JDK : 무료지만 유지보수 어려움
Azul의 Julu : TCK 인증(Oracle해서 해당 라이브러리에 문제가 없다고 인증)을 통과한
Open JDK를 묶어서 배포하는 제 3의 벤더
Open JDK를 사용하므로 라이센스를 요구하지 않음
Julu 설치링크
Java8 window 버전의 msi파일(설치파일)을 다운받는다
설치파일을 실행해서 다운받는데, 환경변수 설정을 위해 설치경로를 메모장에 기록해놓자
시스템 환경 변수에 JAVA_HOME을 추가하고 설치경로를 값으로 넣는다.
Path 환경변수의 내용에 설치경로가 추가되었는지 확인한다.
cmd에서 java -version을 입력해 버전이 알맞게 표시되는지 확인한다.
intellij IDEA community 버전 설치경로
intellij IDEA community 버전을 다운받는다.
Desktop Shortcut을 체크해주고 설치한다.
언어는 Kotlin, JDK 버전은 위에서 설치한 8 버전을 선택해서 프로젝트를 생성한다.
좌측의 1,2,3은 alt+1,2,3 단축키로 선택할 수 있다.
src를 클릭하고 alt+insert로 새로운 파일을 만들 수 있다.
Kotlin Script를 만들면 .kts파일이 만들어지는데 강의에서는 .kt파일이다
그냥 file을 만들고 이름에 .kt를 붙여주면 된다
강의에서는 kotlin 코드를 작성하면 자동으로 화살표 표시가 생기는데(진입점)
현재 버전에서는 그렇지 않다.
따라서 프로젝트의 src폴더를 우클릭하고 make directory as -> sources root
를 선택해주면 진입점이 생긴다.
진입점을 우클릭하고 run as 파일명을 선택하면 실행된다.
Tools -> Kotlin -> Configure Kotlin Plugin Updates로 Kotlin 버전을 계속 업데이트 해주면 된다.
file -> setting 들어가서 여러가지 설정을 해줄 수 있다.
D2coding 폰트를 다운받아서 사용하는 것이 좋다(한글도 갈끔하게 표시된다)
그리고 General에 들어가서 ctrl+마우스휠로 폰트 사이즈를 조정하는 체크박스를 선택해준다.
최초에는 위에서 실행한 방법처럼 실행하던가 alt shift F10으로 실행한다
한번 실행하고 나면 우상단의 화살표를 클릭하거나 shift F10으로 실행할 수 있다.
fun main() {
println("hello")
}
fun은 function을 의미하는 키워드이다
main()은 진입점 역할을 하는 함수이고 {}내부에 내용이 정의되어 있다.
Tools > Kotlin > show Kotlin Bytecode
JVM에서 구동되기 위해 bytecode로 변경된 내용을 확인할 수 있다.
바이트코드로 내용이 표시되고, Decompile 버튼을 누르면 바이트코드를 decompile해서 java코드를 얻을 수 있다.
비교해보면 Kotlin 코드가 Java 코드에 비해 훨신 단순하다.
디컴파일된 Java 코드를 보면 Kotlin 파일의 파일명을 이용해서 클래스를 만든 것을 확인할 수 있다. (ex 파일명 : Hello.kt -> 클래스명 : HelloKt)
메소드에 커서를 올리고 ctrl+b를 누르면 해당 함수가 위치한 라이브러리로 이동한다.
println에 커서를 위치시키고 ctrl + b를 누르자 Console.kt 창이 열렸다.
한번 더 수행하면 자바의 라이브러리로 이동한다. 근본적으로는 자바 라이브러리를 사용하는 것이다.
fun main(args: Array<String>) {
println(args[0])
println(args[1])
}
main은 최상위 함수로 실행 진입점이다.
java같은 객체지향 언어는 클래스와 그 내부에 main이 있어야 프로그램 실행이 가능하다.
Kotlin은 클래스가 필요하지 않고 main만 있으면 된다.
main함수는 코드 어시스트에 의해 자동완성 된다.
main + tab : 매개변수 없는 main
maina + tab : 매개변수 받는 main
매개변수인 args는 String을 요소로 갖는 Array 컬렉션이다.
인자를 전달하지 않고 args[0]으로 인자에 접근하면 예외가 발생한다.
우상단에서 파일명의 드롭메뉴를 클릭하고 Edit Configurations를 클릭한다
Program arguments 부분에 인자를 전달한다.
두번째 println에 커서를 위치하고 ctrl+d를 누르면 해당 줄이 다음 줄로 복사된다.
문자열 내 변수를 표현하기 위해서는 ${식}과 같이 표현한다.
fun main(args: Array<String>) {
println("args[0] is ${args[0]}")
val num : Int = 10
println("num is ${num}")
}
문자열 내에서 args[0]과 num을 ${}를 이용해서 표현한다.
실행결과
자료형
변수 선언 키워드
변수 선언 방법
선언키워드 변수이름: 자료형 = 값
val username: String = "Gildong"
위에서 ${}를 이용해 문자열에 변수식을 표현한다고 했는데, 변수가 하나로만 이루어진 식의 경우에는 {}를 생략해서 사용 가능하다.
fun main() {
val name: String = "보노보노"
println("my name is $name")
}
변수 선언 시 자료형을 명시하지 않으면 컴파일러가 값에 따라 타입을 추론한다.
추론된 타입을 확인하려면 변수 선언 부분을 드래그하고 ctrl+shift+p를 누른다
val num1
val num2 = 10
val num3 : Int
num1은 자료형을 명시하지 않았다. 자료형을 지정하지 않은 변수는 사용할 수 없다.
num2는 자료형을 명시하지 않았지만 값에 따라 추론한다. 따라서 사용할 수 있다.
num3는 선언했지만 값을 초기화하지 않았다. 사용 전, 혹은 생성자 시점에서 변수를 초기화해야 한다.
선언할 때는 자료형을 명시해주거나, 자료형을 명시하지 않으면 선언과 동시에 초기화를 해서 자료형을 추론할 수 있도록 해야한다.
fun main() {
val name : String
name = "스트링"
println(name)
}
선언하면서 초기화는 하지 않았지만 자료형을 명시해줬다.
그리고 나서 변수를 사용하기 전에 초기화해준다.
변수 이름의 조건
일반 변수, 함수명은 소문자로 시작하는 카멜 표기법을 사용한다.
클래스, 인터페이스는 대문자로 시작하는 카멜 표기법을 사용한다.
자료형
기본형
가공되지 않은 순수한 자료형 (프로그래밍 언어에 내장)
int, long, float, double 등
-> Kotlin에서는 사용하지 않지만 Java에서 사용함
참조형
Kotlin에서는 참조형 사용
동적 공간에 데이터를 둔 다음 이것을 참조한다.
Int, Long, Float, Double 등
참조형으로 만들어진 자료형은 객체이다. 객체의 주소를 참조해서 사용한다.
직접 데이터를 다루는 기본형이 성능이 더 우수하다.
하지만 기본형을 사용하면 복잡해지기 때문에 Kotlin에서는 참조형을 사용한다.
Complie 과정에서 참조형이 기본형으로 변환된다. (성능을 위해서)
기본 자료형은 Stack 공간에 자료형의 값이 있다.
참조형은 동적 메모리 공간인 Heap에 객체를 생성하고 해당 객체의 주소(객체의 참조주소)를 Stack 메모리 공간에 저장한다.
[부스트코스]기본 자료형과 변수 선언방법(2) 정수형과 실수형
Long은 8byte이므로 64bit이다. 1bit당 0,1의 두 가지를 표현할 수 있으므로
총 2^64의 범위를 표현할 수 있다. 음수가 존재하기 때문에 2^-63 ~ 2^63-1의 범위가 된다.
음수 표현이 빠지므로 양수 쪽으로 값의 범위가 더 넓어진다.
자신이 표현할 수의 범위에 따라 적절한 자료형을 사용하는 것이 좋다.
자료형 추론
정수 자료형의 자료형을 명시하지 않으면 Int의 범위내에 포함된다면 Int형으로 추론한다
기본 자료형이 Int형이기 때문
val num1 = 127
val num2 = 9223372036854775807
num1은 Byte 자료형의 범위에 포함되지만 Int에도 포함되므로 기본 자료형인 Int형으로 추론
num2는 Int 범위를 벗어나서 Long 형으로 추론
val num1 = 123L
val num2 = 0x0F
val num3 = 0b00001011
num1은 L을 붙여서 Long 형으로 추론
num2와 num3는 각각 16진, 2진 표기한 Int형으로 추론
Int보다 작은 범위의 자료형을 사용하고 싶다면 자료형을 명시해야 한다.
val num1 : Byte = 127
unsigned 와 unsigned Long을 나타내기 위해서 값 뒤에 u와 uL을 붙인다.
큰 수를 읽게 쉽기 하기 위해 값 중간에 under bar (_)를 넣을 수 있다. 값에 아무 영향을 미치지 않는다.
Double : 8byte
Float : 4byte
실수는 기본 자료형이 Double 형으로 명시하지 않으면 기본적으로 Double 형으로 추론된다.
Float형으로 사용하고 싶으면 자료형을 명시해 주거나 값 뒤에 F를 붙여줘야 한다.
val num1 = 3.14F
소스 코드상의 실수 표현
뒤에 f나 F를 붙여서 float형을 표현한다.
3.14E+16
E+16은 10^16을 의미함. +16이므로 소숫점을 오른쪽으로 16칸 이동하면 된다.
IEEE 754 표준 방식에 따라 실수를 표현한다.
부호 비트 MSB(most significant bit)는 맨 앞자리에 0,1로 표현된다 (0이 양수, 1이 음수)
지수부와 가수부를 표현하기 위한 자리가 나눠져 있다.
예시
-12.375(10진법)
float 형식으로 나타내보자.
부호 : 음수 이므로 MSB는 1이된다.
부호를 제외한 12.375를 2진법으로 변경한다 1100.011(2)
소숫점을 이동시킨다. 1.10011 * 2^3
1을 제외하고 지수부, 가수부로 나타낸다.
지수부는 8bit인데 127이 0의 기준이 된다. 즉 01111111이 0을 표현하는 값이다.
지수부가 3이므로 01111111에 3을 더한 10000010이 지수부가 된다.
가수부는 10011이 되고 남는 자리는 전부 0이된다.
1 10000010 10011000000000000000000
msb 지수부 가수부
fun main() {
var num : Double = 0.1
for (i in 0..999){
num+=0.1
}
println(num)
}
실행결과
100.09999999999859
값이 100이 나와야 하는데 뒤에 소숫점이 생겼다.
지수부와 가수부에 제한이 있기 때문에 오차가 발생하는 것이다.
음수표현을 위해서 각 자리수에 1의보수를 취해서 얻은 값에 1을 더한다
(0은 1로 1은 0으로 바꾸고 나서 1을 더한다)
a-b를 a+(b의2의보수)로 덧셈 연산으로 뺄셈을 표현할 수 있다.
Boolean : 1bit : true, false
fun main() {
val isTrue = true
val isFalse : Boolean
}
boolean 자료형은 보통 변수명 앞에 is를 붙인다.
isTrue는 값이 true이기 때문에 컴파일러가 Boolean형으로 추론한다.
Boolean 자료형을 선언만 하고 초기화하지 않을 것이면 꼭 자료형을 명시해준다.
char : 2byte : 0~2^15-1
마찬가지로 선언만 할 경우 자료형을 반드시 명시한다.
사실 이건 모든 자료형에 대해 해당되는 내용이다.
String으로 선언된다. 문자의 배열이다.
String Pool이라는 공간에 구성된다.
fun main() {
var word1 : String = "Hello"
word1 = "ByeBye"
word1[2]
println(word1)
println(word1[2])
word1[3]='k' //에러 발생
}
word1은 var로 선언되었다.
Heap 메모리의 String Pool 공간에 "Hello" String 객체가 생성된다.
word1은 Stack 메모리 공간에서 그 객체의 주소를 값으로 가져서 참조할 수 있다.
이 때, word1 자체는 var로 선언되어서 값을 바꿀 수 있다.
즉 "Hello"를 가리키다가 "ByeBye"를 가리킬 수 있다. (주소를 바꿔 참조하는 대상 변경)
하지만 이미 생성된 String 객체의 내용을 바꿀 수는 없다.
즉 위의 예시코드에서 var로 선언되었으므로 word1은 Hello를 가리키디가 ByeBye를 가리키며 가리키는 대상을 바꿀 수 있다.
하지만 word1[3]이 가리키는 이미 생성된 ByeBye의 3번째 인덱스를 'k'로 바꿀 수는 없다.
fun main() {
var word1 = "Hello"
var word2 = "Yolo"
var word3 = "Hello"
println("word1 === word2 : ${word1===word2}")
println("word1 === word3 : ${word1===word3}")
}
word1 === word2 : false
word1 === word3 : true
== 연산자는 값만 비교하고 ===연산자는 참조를 비교한다.
word1과 word2는 각각 다른 문자열을 가리키므로 참조가 다르다
word3는 "Hello"를 가리키는데 word1도 "Hello"를 가리키므로 이미 word1이 가리키는 "Hello"를 같이 가리키게 되고 따라서 참조가 같다.
문자열을 초기화 할 때도 표현식 ${}를 사용할 수 있다.
Kotlin에서는 (역슬래시)를 탈출문자로 사용한다.
Kotlin의 변수 선언은 기본적으로 null을 허용하지 않는다
변수를 선언만 하고 초기화하지 않으면 null값이 된다.
이러한 변수에 접근하면 NPE가 발생한다.
NPE(null point exception) : 사용할 수 없는 null인 변수에 접근할 때 발생하는 예외
(단순 출력은 상관 없지만 연산할 때)
Int, String등은 null을 허용하지 않는 Non-null type
자료형 뒤에 ?를 붙이면 Nullable type이 된다. Int?, String? 등..
Int와 Int?는 서로 다른 자료형이다.
fun main() {
var word1 : String? = null
val num1 : Int? = null
}
fun main() {
var word1 : String? = null
println("word1 length : ${word1.length}") //예외발생
}
word1은 null인데 length 필드에 접근하면 예외가 발생한다.
fun main() {
var word1 : String? = null
println("word1 length : ${word1?.length}")
}
세이프콜 기호 ?.로 이를 해결할 수 있다.
word1이 null일 경우 ?.를 포함한 뒤부분 연산을 실행하지 않는다.
word1 length : null
fun main() {
var word1 : String? = "abcde"
println("word1 length : ${word1?.length}")
}
null이 아닐 경우 연산을 수행한다.
word1 length : 5
fun main() {
var word1 : String? = "abcde"
println("word1 length : ${word1!!.length}")
}
세이프콜 기호 말고 다른 방법도 있다. !!를 사용하는 것이다.
!!를 사용하면 word1은 절대 null이 아니므로 오류를 무시하라고 명령하는 것이다.
하지만 !!를 사용했는데 word1이 null이라면 NPE가 발생한다.
물론 컴파일하기 전에도 문제가 있다는 것을 표시해준다.
!!는 사용하지 않는 것이 좋다.
fun main() {
var word1 : String? = null
val len:Int = if (word1==null) -1 else word1.length
println(len)
}
위의 식처럼 if-else문을 한 줄에 작성할 수 있다.
코드를 작성했는데 코드에 빨간색 하이라이트가 되는 경우가 있다.
코드에 커서를 위치하고 alt+enter를 누르면 빠른수정이 가능하다.
val len:Int = word1?.length ?: -1
위의 if문을 alt+enter로 빠른수정하자 elvis 연산자를 이용한 식이 되었다.
elvis 연산자는 Kotlin의 연산자로 ?:이다. ?:가 기준이 되어 좌측이 null이 아니면 좌측 내용을 반환하고 null이면
오른쪽 내용을 반환한다.
즉 위의 예시에서는 safe call인 ?.에 의해 word1이 null이면 좌측이 null이므로 -1이 반환되고
word1이 null이 아니면 word1.null을 반환한다.
Nullable과 Non-Null은 함수의 매개변수에도 적용된다.
fun set(a : String, b: String?){
//b is Nullable type
}
서로 다른 자료형은 변환을 거친 후 비교한다.
fun main() {
var a : Int = 10
var b : Double = a //자료형 불일치 오류발생
}
fun main() {
var a : Int = 10
var b : Double = a.toDouble()
}
Int형인 a값을 b 초기화에 사용하면 자료형 불일치 오류가 발생한다
toDouble() 메소드를 이용해서 형변환을 해줘야 한다.
표현식에서도 자료형의 변환이 발생할 수 있다.
fun main() {
val a : Long = 10
val b : Int = 20
val c = a+b
}
Long과 Int를 더한다면 Int는 범위가 더 큰 Long 자료형으로 변환이 된다. 결과는 Long이 된다.
-> 변환 메소드를 사용한 것은 아니고 컴파일러가 자동으로 진행한 것이다
Kotlin의 모든 자료형에 대해 to자료형 메소드로 변환 가능하다.
이중등호 == : 값만 비교한다
삼중등호 === : 값과 참조 주소를 비교한다
fun main() {
val a : Int = 123
val b : Int = 123
println(a==b)
println(a===b)
}
a와 b는 값도 같고, 참조 주소도 같다
Int는 내부적으로 기본자료형 int로 처리된다.
즉 Stack 메모리 공간에 123이라는 값을 저장한다.
따라서 참조주소의 내용이 123으로 같게된다.
fun main() {
val a : Int = 123
val b : Int? = 123
println(a==b)
println(a===b)
}
Nullable type과 None Null type은 다르다
Int? 형은 내부적으로 객체다. 따라서 참조주소는 다르다
기본자료형과 참조자료형의 메모리구조
기본자료형은 주소가 아닌 값을 Stack 메모리에 저장한다.
반면에 Nullable Type은 참조형변수이고 객체의 레퍼런스이다. Heap 메모리에 객체가 생성되고 생성된 객체의 주소를 Stack 메모리에 저장한다.
Kotlin에서는 참조형 변수라도 값이 -128~127사이의 값이면 캐시에 값을 저장하므로 기본형 변수와 방식이 같아진다.
fun main() {
val a : Int = 128
val c : Int? = a
val d : Int? = a
println(c==d)
println(c===d)
}
true
false
a는 기본 자료형으로 Stack 메모리 공간에 128 값을 저장한다.
c와 d는 참조 자료형으로 Heap 메모리 공간에 각각 객체를 만들고 참조한다
따라서 참조주소가 달라서 ===연산의 결과가 false이다
구체적으로 명시되지 않은 자료형을 자동 변환
fun main() {
var num : Number = 12.34 //Float
num = 30 //Int
}
fun main() {
val num = 123
if (num is Int){
println(num)
}
else if (num !is Int){
println("not int")
}
}
is 키워드를 이용해서 num의 자료형을 확인할 수 있다.
num !is Int는 num이 Int가 아닐 때를 의미한다.
자료형이 정해지지 않은 경우, 모든 자료형의 뿌리(부모클래스)이다.
Any는 필요한 자료형으로 언제든 스마트 캐스트 된다.
fun main() {
var a : Any =3
println("${a}의 javaClass : ${a.javaClass}")
a = 10L
println("${a}의 javaClass : ${a.javaClass}")
a = "Hello"
println("${a}의 javaClass : ${a.javaClass}")
}
3의 javaClass : class java.lang.Integer
10의 javaClass : class java.lang.Long
Hello의 javaClass : class java.lang.String
.javaClass 필드를 이용해서 자료형을 알아낼 수 있다.
인자를 Any 형으로 받는 함수
fun main() {
show(10)
show("Hello")
show(1.23)
}
fun show(a : Any){
println("${a} : ${a.javaClass}")
}
함수의 인자를 Any형으로 받으면 모든 자료형의 인자를 받을 수 있다.
산술, 대입, 증가, 감소, 비교, 논리
전위연산 : ++num : 먼저 1 증가 후 연산
후위연산 : num++ : 다른 연산이 끝난 후 1 증가
fun main() {
var a : Int = 0
var b = ++a
var c = a++
println("a = ${a}\nb = ${b}\nc = ${c}")
}
a = 2
b = 1
c = 1
주의 : java에서는 ==를 이용해서 값과 참조를 모두 비교한다는 점이 Kotlin과 다르다.
맨 왼쪽 비트는 MSB로 부호비트로 사용한다
0: 양수, 1: 음수
32bit를 사용한다면 31bit를 값 표현에 사용한다.
shift 연산은 부호비트를 유지하며 나머지 비트들을 shift한다
앞에 u가 붙으면 부호비트까지 같이 shift한다.
xor : 두 비트가 서로 다를 경우에만 결과가 1
비트 연산은 표에서 처럼 함수표현법을 써도 되고 중위표현법을 써도 된다.
4 shl 3 은 4.shl(3)과 같은 표현이다.
fun main() {
val x = 4
val y = 0b0000_0110 // 6(10)
val z = 0x0F // 15(10)
println("x shl 2 = ${x shl 2}")
println("y shr 2 = ${y shr 2}") //0b0000_0001
println("z.inv() = ${z.inv()}")
}
x shl 2 = 16
y shr 2 = 1
z.inv() = -16
fun을 이용해서 선언하고 반환값의 자료형도 : 뒤에 작성해준다.
반환값이 없을 경우 Unit을 사용한다.
Java의 void는 반환값이 아예 없는 것이고 Unit은 Unit이라는 것을 반환하기는 한다.
Unit 반환형은 생략할 수 있다.
fun main() {
println(sum(1,2))
}
fun sum(a:Int, b:Int) = a+ b
반환식이 하나인 경우 sum 함수처럼 간단하게 표현할 수 있다.
반환 자료형은 a,b가 Int이므로 컴파일러가 추론할 수 있어서 생략한다.
src 폴더에 package를 만들어서 .kt파일을 분리해서 관리하자.
fun main() { //최상위 함수 Top level
println(sum(2,3))
}
fun sum(a: Int, b: Int) : Int{ //최상위 함수
return a+b
}
위 코드에서 main과 sum은 모두 최상위 함수이다.
최상위 함수는 제일 바깥에 있는 함수를 의미한다.
sum을 main의 아래에 선언했지만 sum은 최상위 함수여서 main에서 호출할 수 있다. 즉 최상위 함수는 선언 위치에 상관없이 호출가능하다.
fun main() { //최상위 함수 Top level
fun sum(a: Int, b: Int) : Int{ //지역함수
return a+b
}
println(sum(2,3))
}
sum을 main 내부에 선언하면 main은 지역함수가 된다.
지역함수를 선언하고 나서 호출했으므로 정상적으로 호출된다.
fun main() { //최상위 함수 Top level
println(sum(2,3))
fun sum(a: Int, b: Int) : Int{ //지역함수
return a+b
}
}
지역함수를 아직 선언하지 않았는데 호출하는 상황이다.
sum을 호출할 수 없다.
fun main() {
println(max(1,2))
show("사과")
}
fun max(a: Int, b: Int) : Int{
return if (a>=b) a else b //if-else문을 한 줄에 사용
}
fun show(thing : Any) : Unit{
println(thing)
return Unit
}
show 함수는 반환값이 없다. 반환값이 없으면 반환형이 Unit이다.
return Unit이라는 보이지 않는 반환을 한다.
return Unit과 : Unit은 생략해도 무관하다.
fun max(a: Int, b: Int) = if (a>=b) a else b
fun show(thing : Any) = println(thing)
반환식이 1줄이고 반환형을 추론할 수 있는 함수들은 간단하게 표현이 가능하다.
함수 매개변수에는 default 값을 넣을 수 있다
fun main() {
println(max())
}
fun max(a: Int=10, b: Int=10) = if (a>=b) a else b
10
매개변수에 = 값으로 디폴트값을 지정해주면 인자를 전달하지 않았을 때 디폴트값으로 함수를 호출한다.
fun main() {
println(max())
println(max(b=100))
}
fun max(a: Int=10, b: Int=10) = if (a>=b) a else b
max를 호출할 때 b에만 인자를 전달하고 싶다면 b=값으로 이름을 지정해서 원하는 인자를 전달할 수 있다.
함수의 인자 개수를 정해놓고 받는 것이 아니라 동적으로 받고 싶다면 가변인자를 사용한다.
매개변수를 설정할 때 vararg 변수명 : 자료형 으로 가변인자를 사용할 수 있다.
fun main() {
varargTest(1,2,3,4,5)
}
fun varargTest(vararg a : Int) {
for (num in a){
println(num)
}
}
1
2
3
4
5
Stack 메모리는 높은 주소->낮은 주소
Heap 메모리는 낮은 주소->높은 주소 순으로 사용한다.
Stack에 먼저 메인 함수의 프레임이 생기고 사용하는 지역변수들이 push된다. main 실행 도중에 max 함수를 호출하면 해당 함수의 프레임이 생성되고 함수에서 사용할 지역변수들을 Stack에 push한다.
그리고 사용하고 pop하고 max 함수 프레임도 삭제된다.
동적 객체는 Heap 메모리에 생성된다.
fun main() {
println(ave(1f,2f,3f,4f))
}
fun ave(first : Float=0f, vararg rest : Float) : Double {
println("가변인자 개수 : ${rest.size}")
var sum : Float = 0f
for (num in rest){
sum+=num
}
var avg : Float = sum/rest.size
return (avg+first).toDouble()
}
Float을 표현하기 위해 값 뒤에 f를 붙여준다.
반환형은 Double이므로 toDouble을 이용해 형변환을 해준다.
가변인자 rest의 수를 알기 위해 rest.size를 사용한다.