자바와 달리 코틀린은 클래스 내부에서 main을 선언하지 않고도 main 함수를 실행할 수 있다.
fun main() {
println("Hello Kotlin!")
}
fun main(args: Array<String>) {
println(args[0])
println(args[1])
println(args[2])
}
args를 넣는 경우에는 configurations에서 매개변수 값들을 넣어준다.
Byte, Short, Int, Long
Char, String,
Double, Float,
t/f 표현 Boolean이 있고
코틀린은 primitive data type이 아닌 모두 참조형으로 형을 선언하므로 대문자로 형을 써준다.
코틀린은 자바보다 엄격한 타입체크를 한다.
자바처럼 Double에 Int 형을 넣을 수 없다.
따라서 toDouble()과 같은 메소드를 이용해서 명시해야 한다.
val a = 5.toByte() //Byte
val b = 65.toChar() //Char
val c = 10.toShort() //Short
val d = 10 //Int
val e = 10L //Long
val f = 10.0f //Float
var g = 10.0 //Double
또한 val result = 1L + 3과 같이 Long+Int는 가능한데 이 때의 result 타입은 Long이다.
string을 삼중 따옴표로 감싸면 키보드로 입력한 줄바꿈이 그대로 반영된다.
var ch = '\uAC00'
println(ch) //가
val str = """
여러분
모두 화이팅!
""".trimIndent()
println(str)
결과
가
여러분
모두 화이팅!
trimindent 미사용시
가
여러분
모두 화이팅!
val test1 = "ff"
val test2 = "ff"
if(test1 == test2){
println("true")
}
else
println("false")
자바와 달리 true가 나온다. 코틀린은 연산자 오버로딩을 통해 ==에 equals 메소드가 구현되어 있다.
val i = 100
val test1 = "ff + $i.toString()"
val test2 = "ff + ${i.toString()}"
println(test1)
println(test2)
ff + 100.toString()
ff + 100
val stringSequence = "문자열"
val temp = stringSequence
if(temp is String){
println(temp.substring(1 , endIndex (선택사항)))
자열
typealias Num = Int
와 같이 타입 별명 붙이기가 가능하다.
Any 타입에는 모든 타입을 다 할당할 수 있고, 수치형은 Number타입에 할당할 수 있다.
Number는 할당하는 수치형의 자료형을 자동으로 스마트 캐스팅을 해주어 편리하다.
var test1:Number = 6.2 // test1 은 Double형으로 스마트캐스팅됨.
println("value : $test1 , data type: ${test1.javaClass}")
test1 = 12 //Integer 형으로 스마트캐스트
println("value : $test1 , data type: ${test1.javaClass}")
test1 = 12L //Long 형으로 스마트캐스트
println("value : $test1 , data type: ${test1.javaClass}")
test1 += 12.0f //Float형으로 스마트캐스트
println("value : $test1 , data type: ${test1.javaClass}")
value : 6.2 , data type: class java.lang.Double
value : 12 , data type: class java.lang.Integer
value : 12 , data type: class java.lang.Long
value : 24.0 , data type: class java.lang.Float
val number = 256
if (number is Int) { // num이 Int형일 때
print(number)
} else if (number !is Int) { // number 이 Int형이 아닐 때, !(number is Int) 와 동일
print("Not a Int")
}
println()
is를 통해 타입 체크가 가능하다.
Unit 타입은 함수의 반환값이 없음을 명시적으로 나타낼 때 쓰인다.
Noting은 null만 반환하는 함수나 예외를 던지는 함수의 반환타입을 나타내기 위해 쓰인다.
fun some(): Unit {
print("no return")
}
fun some2(): Nothing {
return null
}
fun some3(): Nothing {
throw Exception()
}
fun hi(){
val ten : Int = 10
var b : Int = 9
val hundred = 100
var name = "Lee"
b = 10
println(hundred)
println(ten)
println(b)
}
val은 값을 바꿀 수 없는 상수,
var은 값이 바뀔 수 있는 변수를 저장하는 형이다.
코틀린은 컴파일 타임에서 타입 체크를 한다. 변수를 선언할 때 val ten : Int = 10
처럼 형을 명시해주거나 val hundred = 100
처럼 형을 따로 명시안해줄 수도 있으나 val이나 var을 꼭 써서 선언해야 한다.
var ten
은 안되고
var ten : Int
와 같이 형을 선언해준다면 할당없이 선언이 가능하다. 이는 실행되는 함수 내에서만 해당하며 클래스 내부 변수 등에서는 불가능하다.
lateinit var data1 : Int //오류
lateinit val data2 : String // 오류
lateinit var data3 : String // 성공
lateinit은 선언만 하고 초기화는 하지 않고 싶을 때 선언할 수 있으며 var
키워드로 선언한 데이터여야 하고, Int,Long,Short,Boolean,Byte 등의 자료형에는 사용 못한다.
뷰 바인딩을 할 때
private lateinit var binding: ActivityMainBinding
으로 변수 초기화를 미루고 oncreate에서 초기화 할 때 등 사용한다.
val data4 : Int by lazy {
println("Hello WOrld")
10
}
fun main(){
println(data4 + 10)
}
20
by lazy{}
로 중괄호 안에 실행문을 넣어주면 마지막 줄의 실행 결과가 그 변수의 초깃값이 된다. lazy로 되어있는 변수는 호출이 될 때 실행문이 실행된다.
lazy는 변수 초기화시 코드 블럭을 수행하는 것이므로, 멀티쓰레드 동작 시 고려가 있어야 한다.
SYCHRONIZED: 락을 사용해 단일 스레드만이 사용하는 것을 보장(기본값)
PUBLICATION: 여러 군데서 호출될 수 있으나 처음 초기화된 후 반환 값을 사용
NONE: 락을 사용하지 않기 때문에 빠르지만 다중 스레드가 접근할 수 있음
`private val model by lazy(mode = LazyThreadSafetyMode.SYNCHORNIZED)
class Person {
lateinit var name: String // lateinte을 위한 선언
fun test() {
if(! ::name.isInitialized) { // 프로퍼티의 초기화 여부 판단
println("not initialized")
} else {
println("initialized")
}
}
}
함수 스코프 내에서 말고 클래스 멤버변수에서만 isInitialized를 사용할 수 있다.
참고로 단순히 name이 아니라 ::name이라고 선언하는 이유는 name이라는 String의 메소드에 접근하는 것이 아닌 property로서의 name의 메소드에 접근하려는 것이기 때문이다.
fun main() {
val name = "Lee"
val age = 25
println("my name is $name .Nice to meet you.")
println("my age is ${age}years old")
}
$표시와 중괄호로 변수를 묶어 string형태로 변수를 출력할 수 있다.
중괄호로 묶지 않고도 가능한데, 이 때 $뒤에 나오는 변수명 뒤에 다른 말을 쓰려면 공백으로 구분되어야 한다.
$를 문자 형태로 직접 출력하려면 백슬래시
\
를 앞에 써주면 된다.
===
두 개 항의 참조가 같으면 true, 아니면 false (!==
)
나머지는 자바와 동일
package com.example.myapplication
fun main() {
helloWorld()
println(add(1,3))
}
//1.함수
fun helloWorld(): Unit {
println("Hello World!")
}
fun add(a: Int, b: Int): Int {
return a+b
}
C, Java와 마찬가지로 kotlin도 main함수가 실행된다.
함수를 정의할 때는 리턴값에 상관없이 fun
을 붙여주며, void 함수로 리턴값이 없는 경우에 위처럼 중괄호 이전에 : Unit
을 일반적으로 써준다. 써주지 않아도 실행에 문제는 없다.
fun add(a: Int, b: Int = 10): Int {
return a+b
}
위 함수를 보자. a,b를 정수형으로 매개변수로 받으며 위와 같이 a: Int
의 형식으로 형의 첫글자를 대문자로 적어주며, 마지막에 return 하는 형을 : Int
로 선언전에 적어준다. 매개변수의 기본값도 줄 수 있어 선언하지 않을 시 기본값으로 적용된다.
fun add1(x1:Int, x2:Int):Int = x1 + x2
fun maxBy(a : Int, b: Int) : Int{
if(a>b){
return a
}
else
return b
}
대체로 C와 사용 방식이 유사하다.
C에서 삼항 연산자를 사용하는 것과 유사하게
fun maxBy2(a : Int, b: Int) = if(a>b) a else b
와 같이 간결한 함수 작성도 가능하다.
fun checkNum(score : Int){
when(score){
0 -> println("this is 0")
1 -> println("this is 1")
2,3 -> println("this is 2 or 3")
else -> println("I don't know")
}
var b = when(score){
1->1
2->2
3->3
else -> 3
}
}
switch 문과 유사하게 변수의 여러가지 값에 따라 출력이나 리턴값을 지정해 줄 수 있다.
이 때 var b
와 같이 변수에 리턴값을 저장할 때 when을 사용한다면 꼭 else문이 포함되어야 한다.
when (x) {
1 -> println("x == 1")
2, 3 -> println("x == 2 or x == 3")
in 4..7 -> println("4부터 7사이")
!in 8..10 -> println("8부터 10사이 아님!!")
else -> println("x는 1이나 2가 아님")
}
// x == 1
또한 조건 여러개에 해당하는 값이어도 앞의 조건만 만족하면 바로 return을 한다.
위 예시에서 x = 1이라면 "8부터 10사이 아님!!"이 출력되지 않는다.
when(score){
in 90..100-> println("You are genious")
in 10..80-> println("You are not bad")
else -> println("I don't know")
}
if(score in 90..100){
println("You are genious")
}
in 90..100
이라는 것은 90<=score<=100
과 같은 의미이다.
코틀린에서 statement 와 expression 를 구분하는 방법은 값을 만들어 내는 차이에 있습니다.
expression 은 값을 만들어내고 또 다른 expression 의 하위 요소로 계산에 참여할 수 있습니다.
반면에 statement 는 최상위 요소로 존재하며 어떠한 값도 만들어내지 않습니다.
예를 들어 Java에서는 if문은 값을 리턴하는 것이 아니므로 statement 이지만 코틀린에서는
if(a>b) a else b
이와 같이 반환값이 있는 expression이 될 수 있다.
array는 값을 바꿀 수 있는 자료구조로
val array = arrayOf(1,"2",3.4f)
val num : Array<Int> = arrayOf(1,2,3,4)
와 같이 정의한다.
자료 인덱스에 대한 접근은 println(num[0])
, println(num.get(0))
와 같이 한다. 또한 위처럼 다양한 타입의 값들을 array에 넣어줄 수 있다.
다만 Array<Int>
와 같이 구체적으로 타입을 명시해주면 int만 넣어야 한다. 타입을 명시 안하면 자동으로 Array<Any>
로 인식한다.
반면 list는 mutable list가 아닌 이상 값을 추가하거나 변경할 수 없으며
val list = listOf(1,2,11L)
와 같이 정의한다.
값을 바꿀 수 있는 mutableList로 리스트를 만들기 위하여
val mutablelist = arrayListOf<Int>(1,2,3)
mutableList.add(4)
mutableList.add(5)
와 같이 arrayListOf
함수를 사용하여 만들어주며, 이때 값을 바꿀 수 있다.
여기서 값을 바꾸는 데도
val
로 리스트 변수를 선언할 수 있는 이유는 리스트에 값을 추가하는 것이 변수의 주소값에는 영향을 주지 않기 때문이다.
물론mutableList = arrayListOf(4,5,6)
과 같이 재할당은 할 수 없다.
val items = listOf("a","b","c")
println(items.indices)
인덱스 접근메서드 이다. 0..2를 출력한다.
fun forAndWhile(){
val students = arrayListOf<String>("joyce", "Lee" , "Kim")
for(name in students){
println("${name}")
}
}
기존의 C 사용법과 유사하다.
for(i in 1..10){
println(i)
}
in 을 사용하여 1~10까지의 range로 반복문을 표현할 수도 있다.
for(i in 1..10 step 2){
println(i)
}
1부터 10까지 2의 간격씩 i를 더해가며 반복할 수도 있다.
for(i in 10 downTo 1){
println(i)
}
큰 수에서부터 작은 수로 가면서 반복할 때는 위와 같이 downTo
를 사용하면 된다.
for(i in 10 downTo 1){
println(i)
}
for(i in 1 until 100){
println(i)
}
1..100과 달리 100이 포함되지 않는다.
//val students = arrayListOf<String>("joyce", "Lee" , "Kim")
for((index : Int ,name : String) in students.withIndex()){
println("index = ${index} name = ${name}")
}
결과:
index = 0 name = joyce
index = 1 name = Lee
index = 2 name = Kim
withIndex()
메서드로 인덱스와 해당값을 함께 반환할 수도 있다.
var index : Int = 0
while(index<10){
println("Index = ${index}")
index++
}
while문도 크게 C와 다르지 않다.
중첩된 반복문에서 완전히 탈출하고 싶을 때 사용하면 유용하다.
Java에서 흔히 발생하는 런타임 에러인 NPE = Null Pointer Exception
, 즉 널값을 가리키는 포인터 문제를 컴파일 단계에서 바로잡아 개발하기 수월하기 위하여
Kotlin에서는 ?
를 통해 이를 해결하고 있다.
var name = "Lee"
//var name : String = null
var nullName : String? = null
var nameInUpperCase = name.toUpperCase()
var nullNameInUpperCase = nullName?.toUpperCase()
일반적인 변수에는 null을 저장할 수 없으나, 형 뒤에 (형 선언을 꼭 해줘야 ? 사용 가능) ?를 붙여줌으로서 null지정을 할 수 있다.
쉽게 생각하면 nullName에 올 수 있는 타입은 String과 null이 있는 것이다.
null값에는 적용할 수 없는 함수인 toUpperCase메서드의 경우에도 메서드 사용 앞에 ?
를 써주면, null 값일 때는 그대로 null 을 반환하고 오류가 출력되지 않도록 해줄 수 있다.
var temp: String? = null
val size2 = if (temp != null) temp.length else -1
참고로 위 코드와 같이 코틀린에서는 if가 return 값을 가지므로 이를 바로 할당할 수 있다. (따라서 자바와 같이 삼항연산자가 필요없음)
변수 뒤에 붙어 이 변수가 null인 경우에 사용할 기본값을 지정해주는 연산자이다.
val lastName : String? = null
val fullName = name + (lastName?: " No lastName")
println(fullName)
결과
Lee No lastName
fun getName(str: String?) : String {
val name = str ?: "Unknown"
return name
}
위 함수는 엘비스 프레슬리 연산자를 사용해 null 값이 반환 될 일이 없으므로 반환 타입을 String?이 아닌 String이라고 할 수 있다.
변수가 절대 null값이 오지 않을 것임을 명시해주는 연산자이다
fun ignoreNulls(str : String?){
//val NotNull : String = str
val NotNull : String = str!!
val upperNotNull = NotNull.uppercase()
println(upperNotNull)
}
!!없이 val NotNull : String = str
만을 쓰면 nullable한 변수를 nullable하지 않은 변수에 넣으려 했으므로 오류가 나지만, !!
를 붙여줌으로써 str은 null값이 절대 아닐 것임을 프로그램에게 알려준다. 따라서 uppercase()
와 같은 함수를 사용하더라도 null 값이 절대 안올 것이기에 ?
를 안 붙여줘도 된다.
위 방식은 매개변수로 null 이 안올 것임을 명시하고 있기 때문에 null값이 실제로 들어오면 nullPointerException이 나게 된다.
fun ignoreNulls(s: String?) {
val sNotNull: String = s!!
println(sNotNull.length)
}
ignoreNulls(null)
오류
Exception in thread "main" java.lang.NullPointerException
val email : String? = "james_22@naver.com"
email?.let{
println("my email is ${email}")
}
let은 let이 쓰인 해당 변수가 null이 아닌 경우 그 변수를 식 안으로 들여와 쓸 수 있다.