Kotlin 을 배워보자 part2(if, when, map, for, while, array, list, singleOrNull, ranges, return with label)
이전 글에서 변수선언 방법, Basic Types, Null, 함수, Interpolation, Lamdas,
Type Check와 SmartCast 및 형변환에 대해서 정리해 보았는데요.
이번 글 에서는 if, when 등의 conditional과 for, array나 list, ranges에 대해서 다루도록 하겠습니다.
이번 글 이해를 위해서는 이전 글에서 다루고 있는 주제들에 대한 이해가 필요하니,
필요하신 분들은 아래 링크를 참조해 주세요.
>> Kotlin을 배워보자 part1(Basic Types, Function, Null, 타입 변환, Lambda)
먼저 if문에 대해서 보도록 하겠습니다.
1. If
if나 if else를 Java에서 사용하던 방법은 Kotlin에서도 동일한데요.
아래 이미지의 largerNumber변수와 같이,
if문을 하나의 expression으로도 사용할 수 있습니다.
Java에서의 삼항연산자랑 비슷하다고 할 수도 있겠네요.
2. while과 doWhile
while과 doWhile은 쉬운 것 같으면서도 이해가 않될수도 있는데요.
가장 큰 특징은 while은 조건에 맞는 경우에만 아래와 같이 수행이 되구요.
doWhile은 일단 do가 실행이 우선되고,
while조건에 맞을 때까지는 반복이 되는 것 입니다.
do while에 대해 이해하기 위해,
while의 조건에 한번도 맞지 않음에도,
do내부의 print명령어가 실행된 것을 볼 수 있습니다.
무한반복을 위한 while(true)문도 Java에서와 동일하게 사용가능하구요.
3. when
when은 Java에서의 switch와 비교할 수 있는데요.
switch보다는 받아들일 수 있는 인자가 훨씬 다양하므로 활용하기가 더욱 좋습니다.
게다가 switch문에서와 같이 break문을 작성할 필요도 없고 말이지요.
아래와 같이도 사용이 가능한데요.
위에서 정리한 ranges와 같이 사용하거나,
11, 12에서 볼 수 있듯이, 콤마(",")를 사용해서 Java의 switch에서 break를 걸지 않고 사용하는 것과 같은 효과를 얻을수 있습니다.
아래는 when의 콤마(,)를 이용해서 NullPointer와 IOException에 같은 대응을 하는 코드입니다.
(물론 실제로 프로덕트에 아래와 같은 일을 할 가능성은 없지만 말이지요)
is같이 타입체크를 하고 나서 분기를 탈수도 있습니다.
part1에서 정리한 smartCast가 적용되기 때문에 형변환 없이도 startsWith같은 멤버 함수를 사용할 수 있습니다.
한가지 주의해야 할 것은, when이 expression으로서 사용되어 질 때는,
else에 어떤 값을 꼭 넣어주어야 한다는 점 입니다.
물론, boolean값을 체크하는 경우, 즉 true인 경우와 false인 경우를 정의하였다면,
이 때는 expression으로 사용되더라도 else가 필요하지는 않습니다.
이 부분은 compiler의 도움을 얻는 부분이므로 크게 걱정하지는 않아도 될 것 같습니다.
when은 if-elseif문을 대신할 수도 있는데요.
인자를 넣어주지 않으면 if-elseif-else문을 when으로도 구현 가능합니다.
4. List, filter, singleOrNull 그리고 For문
4-1. list
Kotlin에서는 listOf()메소드 이용해서 list를 생성할 수 있는데요.
여기에 루프를 돌면서, concurrentModificationException없이
값을 변경하는데 도움이 주는 map메소드를 사용할 수 있구요.
인덱스 값도 같이 사용할 수 있는 mapIndexed메소드도 존재합니다.
참고로 리스트에 아이템을 추가할 때는
add()함수도 사용할 수 있지만,
아래와 같이 "+=" 연산자도 사용할 수 있습니다.
a += 1 은 프로그래밍언어에서 a = a + 1을 축약해서 표현하는 방식인데,
kotlin의 list에서 아이템을 추가하는데 사용할 수 있습니다.
(참고로 a =+ 1은 그냥 양수1을 의미합니다.)
변경이 가능한 리스트를 만들기 위해서, mutableListOf()함수를 사용하였습니다.
물론 arrayListOf()함수를 이용해서 arrayList함수로 만들어서 사용해도 무관합니다.
결과는 아래와 같이 나오는 것을 볼 수 있습니다.
개인적으로 += 연산자를 많이 사용하게 되네요.
4-2. list에서 MutableList 또는 arrayList로 변경하기
list에서 mutableList로 바꾸는 방법은 간단합니다.
toMutableList()라는 함수를 사용해주기만 하면 되기 때문입니다.
list.toMutableList()
만약 특별하게 list를 arrayList로 변경해야 한다면 아래와 같은 코드로 쉽게 변경해 사용할 수 있습니다.
아래와 같이 list로 받은 결과를 arrayListOf()함수를 이용해서 array를 만든다음에, arrayList에 addAll()해주면 됩니다.
4-3. filter
아래 이미지와 같이,
리스트에 filter()메소드를 사용하여 필터링을 할 수도 있습니다.
4-4. singleOrNull
filter이외에도 사용하기 편한 API들이 많이 있는데요.
예를 들어, singleOrNull메소드의 경우
리스트에서 하나의 아이템만 찾고자 할 때,
찾으면 값을 넘겨주고, 없으면 null값을 넘겨줍니다.
4-5. sorted & sortedDescending
sorted() 혹은 sortedDescending() 메소드를 이용해서,
아래와 같이 정렬을 할 수도 있구요.
4-6. isNullOrEmpty
당연히도 list가 empty 혹은 null인지 검사하는 API도 존재하느데요.
아래와 같이 isNullOrEmpty()메소드로 확인이 가능합니다.
4-7. iteration
for in문을 사용해서 iteration을 할 수도 있습니다.
index나 value값도 아래와 같이, withIndex()메소드를 이용하거나, indices를 통해서 출력할수 있습니다.
아래와 같은 경우도 볼 수 있는데요.
이런 경우도 있을 수 있구나 하고 가볍게 보고 넘어가시면 됩니다.
enum class인 Colors에는 values()함수를 사용해서 array형태로 가져올 수 있습니다.
이 colors에 indices로 접근한 다음, randomOrNull함수를 통해서 indices가 비어져 있지 않다면 random값을,
만약, 비어져 있다면 null값을 가지게 합니다.
그 다음 elvis식으로 0값을 주게 되어서 문제가 발생하지 않도록 해 줍니다.
참고로, 위의 코드에서 first 람다식은 조건을 만족시키는 첫번째 값을 return해 줍니다.
4-8. sumOf, minOf 그리고 maxOf
sumOf 와 minOf 그리고 maxOf라는 것도 있는데요.
아래와 같이 sumOf안에 무엇을 기준으로 더할것인지만 알려준다면, 리스트아이템들의 특정한 값의 합을 쉽게 구할 수 있습니다.
minOf와 maxOf는 최소와 최대값을 구할 수 있습니다.
4-8. flatten
flatten은 여러개의 리스트들이 모인 리스트들을 풀어주는 것 인데요.
코드를 보면 쉽게 이해할 수 있습니다.
아래와 같이 여러개의 리스트로 만들어진 리스트가 있다고 가정해 보겠습니다.
실행해보면, 펼쳐져서 하나의 리스트로 된 것을 볼 수 있습니다.
5. ranges (range, until)
5-1. range
Kotlin에서는 아래와 같이 ".."기호를 이용해서 range를 사용할 수 있는데요.
아래는 1부터 8까지의 숫자를 range를 사용해 표현한 것입니다
이렇게 특정범위의 숫자를 간결하게 표현할 수 있는 것 이지요.
아래와 같이 for in문에서도 유용하게 쓸수 있습니다.
if문을 이용해서 범위안에 들어가 있는지도 확인이 가능합니다.
5-2. until
위에서 본것 처럼 range는 a부터 b까지를 a..b 로 표현하고 있습니다.
until은 뭐가 다른 걸까요? 코드를 실행해보고 차이를 보겠습니다.
실행해보면 다음과 같은 결과가 나오는데요.
"1 until 6" 이라면 1부터 5까지를 말합니다.
a..b 라면, a부터 b의 직전까지 인 것이지요.
until은 다음과 같은 표현도 가능합니다.
6. Map
kotlin에서는 map을 만드는 쉬운 방법도 제공해 주는데요.
아래 이미지와 같이 to를 이용해서 만드는 방법도 있구요.
Pair객체를 이용해서 만들수도 있습니다.
map을 만들어서 아래와 같이 ascending(오름차순)으로 정렬한다음 결과를 print하는 것도
매우 간단하게 할 수 있습니다.
이 밖에도 list를 map으로 만들수 있는 방법도 있는데요.
특히 리스트에 중복된 데이터들이 존재할 경우 유용하겠지요.
이 때는 groupBy 를 이용해주면 됩니다.
map은 key와 value로 되어있는 값인데 어떻게 list로 map을 만든다는 것 일까요?
공식문서에는 아래와 같이 되어있는데요.
groupBy에 주어진 keySelector를 기준으로 해서,
리스트의 엘리먼트들을 map으로 적용시킨다는 것인데요.
실제 코드를 보면서 이해해 보겠습니다.
아래는 공식문서의 예제를 간단히 실행해 본 것인데요.
결과는 다음과 같습니다.
groupBy에 주어진 selector는 length입니다.
그래서, key값이 1, 2, 3, 4로 들어간 것 이지요.
아래와 같은 것도 가능한데요.
groupBy로 Map을 만든 데이터를 다시 변경하는 것인데요.
이제는 Map타입의 자료이므로, 인자로 키와 밸류 2개를 사용할 수 있겠습니다.
그래서 아래에서는 length와 str 2개가 인자로 사용이 되었지요.
결과 값은 아래와 같이 나오는 것을 볼 수 있습니다.
7. Array
배열(Array)은 arrayOf함수를 이용해 생성할 수 있구요.
클래스로서 get과 set함수를 가지고 있습니다.
"[ ]"를 이용해서 접근하거나 get함수를 이용할 수도 있습니다.
마찬가지로 set도 아래와 같이 사용가능합니다.
null을 사용하지 않는,
객체가 아닌 java의 primitive 타입의 array를 사용하고자 할 때는
아래와 같이 IntArray를 사용하면 됩니다.
0으로 초기화 된 값이 들어가 있는 것을 볼 수 있습니다.
part1의 글에서도 다루었던 lambda식도 이용해 볼 수 있는데요.
역시 인자는 하나이므로, it 키워드를 이용해서 filter하거나 sort혹은 mapping을 한다음
foreach 메소드로 iteration해서 출력할 수 있습니다.
8. Label로 표현하는 Break와 Continue 그리고 return
8-1. Return, break, 그리고 continue
return은 함수에서, break와 continue는 반복문에서 사용을 하는데요.
구분 | 내용 |
return | 가장 가까운 함수에서 return하고 나갑니다. |
break | 가장 가까운 반복문을 중단시킵니다. |
continue | 가장 가까운 반복문에서 다음단계로 넘어갑니다. 예) 1..5 라면 3에서 continue를 시키면 아래부분의 코드는 실행시키지 않고, 4로 넘어가게 됩니다. |
8-2. Break와 Continue
이번에는 label을 이용한 break혹은 conitinue에 대해서 정리해 보려고 하는데요.
lable은 무엇일까요?
label은 본인이 만든 identifier와 "@"로 구성합니다.
label을 정의할 때는 "identifier+@",
호출할 때는 "@+identifier"로 표시하구요.
중첩되어 for문에서,
안쪽의 for문에서 break를 걸어서 두개의 for문을 모두 break걸리게 할 수 있는데요.
아래와 같이 j가 2에 도달하면 두개의 중첩된 for문이 모두 break가 걸리게 할 수 있는 것이지요.
원래대로 하면 바깥의 for문은 멈추지 않고 계속 돌아가게 됩니다.
continue도 마찬가지로 스킵하고자 하는 반복문의 iteration에서 "continue@loop"와 같이 사용해주기만 하면 됩니다.
8-3. return에서의 label
A. Label이 필요하지 않는 익명함수
return의 경우 label을 사용이 필요하지 않는 경우를 보도록 하겠습니다.
익명함수를 사용하는 경우인데요.
아래와 같이, foreach문에서, 3일 경우에만 print하지 않고 return되도록 해보면,
아래와 같이 정상적으로 동작하는 것을 볼 수 있습니다.
B. Lamda와 Label
그런데 이것을 lamda식으로 해보니 return이 제대로 동작하지 않고 에러가 납니다.
이럴때 label을 사용하면 아래와 같이 원래 익명함수를 사용하던 때와 같은 결과를 얻을 수 있습니다.
아래에서는 cut이라는 label을 이용하였는데요.
대세인 lamda를 사용하려면 label사용법을 잘 알아두어야 겠네요.
C. Label을 정하지 않는 경우
label을 정하지 않을경우에는,
lambda가 사용되어진 함수의 이름과 동일한 값이 기본값으로 들어가 있어서,
그 이름을 호출하면 되는데요.
아래에서는 foreach가 기본으로 label로 되어 있어서,
forEach를 호출해서 사용할 수 있습니다.
이 기본label에 대한 정보는 안드로이드 스튜디오 같은 IDE툴이 도와주므로,
이것이 가능하다는 것만 알고 있으면 어렵지 않게 사용할 수 있습니다.
아래와 같이 return해주면서 특정값을 반환하도록 해 줄 수도 있습니다.
9. 정리
이번 part2에서는 if, when, while,for, array, list, ranges등에 대해서 정리해 보았구요.
다음 part3에서는 class에 대해서 정리해 보도록 하겠습니다.