이번 글에서는 코틀린에서 연산자, 조건문, 반복문을 사용하는 문법을 소개하도록 하겠습니다.
자바언어에서 연산자, 조건문, 반복문을 모두 학습하고 사용해 본 상태일테니 가급적 코틀린에서 특이하게 변경된 문법만 살펴보는 형식으로 글을 작성하겠습니다.
새로운 코틀린 파일 Test03Basic3.kt 를 만들고 문법을 알아보겠습니다.
프로그램의 시작인 main() 함수까지 작성하겠습니다.
3. 연산자 - 코틀린에서 달라진 것들
기본적으로 코틀린은 자바의 문법적 표기법만 함수형언어 형식의 모던 코딩 방식으로 변경한 것인만큼 사용하는 연산자는 별로 차이가 없습니다.
자바와 마찬가지로. 산술연산자, 비교연산자, 논리연산자, 비트연산자, 증감연산자, 복합대입연산자, 형변환연산자를 가지고 있습니다. 단, 자바와 다르게 삼항(조건)연산자는 문법적으로 제공하지 않습니다. 다음에 소개할 if 조건문법이 이를 대체합니다.
이제 연산자를 사용할 때 자바와는 다른 몇가지 특이한 점을 살펴보겠습니다.
1) 숫자타입들간의 연산은 자동 형변환이 수행됨[작은것->큰것]
println(10 + 3.14) //출력: 13.14 - Int + Double ==> 10.0(Dobule) + 3.14(Double)
2) 숫자타입이 아닌 자료형과는 자동 형변환이 수행되지 않음. [Boolean, Char]
println(10 + true) //ERROR - Int + Boolean (X)
println(10 + 'A') //ERROR - Int + Char (X) ~ 자바와 다르게 Char타입은 정수 65로 인식되지 않음
3) 자료형을 체크해주는 연산자 is
var n4 = 10 //자동추론
if (n4 is Int) { //Int가 맞으면 true
println("n4변수는 Int타입 입니다.")
}
var s3: String = "Hello"
if (s3 is String) println("s3변수는 String 타입입니다.") //String 맞기에 true
String 과 String? 는 같은 타입의 종류입니다. 그래서 String? 도 true로 결과를 인식함
if (s3 is String?) println("s3변수는 String 타입입니다.")
자료형이 맞지 않는 가를 체크하는 !is 도 있습니다.
if( s3 !is String) println("s3변수는 String 타입이 아닙니다.")
변수의 자료형과 다른 자료형을 is연산자로 체크하면 문법적으로 에러를 표시함
if( n4 is String){ } //ERROR - Incompatible types: String and Int
결국 같은 자료형에 대해서만 타입을 체크할 수 있다는 건데요. 그렇다는 건 변수의 자료형을 알고 있다는 것이겠네요.
그럼 큰 의미는 없겠군요??
그렇지 않습니다. 사실 is연산자는 Any타입에 대한 식별로 많이 사용됩니다.
var obj:Any //Any타입이기에 어떤 데이터도 참조 가능
obj= 10 //Int 데이터
// Any타입 변수에 저장된 데이터의 타입을 체크
if(obj is Int) println( "${obj}는 Int입니다.")
if(obj is Double) println( "${obj}는 Double입니다.")
//결과 : 10는 Int입니다.
이번에는 obj에게 실수형 데이터를 대입해 보고 체크해 보겠습니다.
obj= 10.5 //Double 데이터
if(obj is Int) println( "${obj}는 Int입니다.")
if(obj is Double) println( "${obj}는 Double입니다.")
//결과 : 10.5는 Double입니다.
Any타입에는 어떤 자료형도 참조하는 것이 가능하기에 실제 참조하는 데이터 또는 객체의 종류에 따라 대응하는 기능을 사용해야 할때 많이 사용하는 연산자 입니다.
이 is 연산자는 자바의 instanceof같은 기능으로도 사용이 가능합니다.
4) 자바의 instanceof같은 기능으로 사용하는 is 연산자
코틀린에서의 클래스와 객체에 대한 소개는 아직 진행전이지만 연습을 위해 이름(name)과 나이(age)를 저장할 수 있는 Person 클래스를 설계해보겠습니다. 자바와 다른 문법적 차이는 주석으로 간략하게 소개하겠습니다.
class Person{
//코틀린에서는 멤버변수를 속성[Property]라고 명명함.
//주의!! 프로퍼티는 반드시 초기화가 되어 있어야 함.
var name:String = "sam"
var age= 20
}
이제 Person 객체를 생성하고 is 연산자가 사용되는 모습을 살펴 보겠습니다.
var p= Person() //코틀린에서는 객체를 생성하는 키워드 new 를 명시하지 않음
if(p is Person){
println( p.name + " " + p.age ) //출력: sam 20
}
멤버변수의 출력을 문자열탬플릿 으로 하면 출력서식을 만들기 조금 더 용이합니다.
var p= Person()
if(p is Person){
println( p.name + " " + p.age )
println( "이름 : ${p.name} 나이: ${p.age}")
}
//**출력**
이름 : sam 나이: 20
is 연산자 사용의 특이한 점이 있습니다. is연산자로 특정 타입이라는 것이 확인되었다면 true인 영역안에서는 Any 타입 변수가 그 타입으로 인식됩니다.
Any타입에 Person객체로 인식하여 사용하기
var obj2:Any
obj2= Person()
obj2.name="aaa" //에러는 아니지만 Android Studio편집기에서 name변수의 존재를 알려주지 않음. [다만, 업캐스팅 되어 있다면 .연산자로 자식객체의 멤버를 강제로 명시하여 사용이 가능함(자동 형변환 - 권장하지 않음)]
Android Studio 편집기의 입장에서는 obj2 변수는 Any타입이기에 . 연산자를 썼을때 멤버리스트의 존재를 실제 참조하는 Person객체가 아니라 Any 클래스로 지원합니다. 개발자의 입장에서 객체의 멤버명을 모두 외워서 사용하기 어렵기에 편집기의 코드 어시스턴스 기능을 이용하여 코딩하는 경우가 많기에 Any 참조변수가 참조하는 실제 참조객체의 멤버를 사용할 때는 실제 참조객체의 참조변수를 만들어 다운캐스팅으로 형변환한 후 멤버를 사용합니다. 꽤 번거롭습니다.
if(obj2 is Person){ // is의 참 영역 안에서는 obj2가 Any타입이 아니라 Person타입의 참조변수인것으로 편집기가 인식함.
println( obj2.name + " " + obj2.age )
println( "이름 : ${obj2.name} 나이: ${obj2.age}")
}
5) 비트 연산자를 대체하는 메소드와 표기법
코틀린은 비트연산자가 없습니다. 대신 비트연산자에 대응하는 메소드가 있으며 가독성이 좋은 표기법도 제공합니다.
println( 7 | 3) //ERROR - | ( OR 비트연산자) 없음
println( 7.or(3) ) // OR 비트연산에 대응하는 메소드가 Int 타입에 존재함
println( 7 or 3 ) // OR 메소드를 가독성 좋게 마치 연산자처럼 표기하는 것이 가능함.
println( true and false ) // & AND 비트연산에 대응하는 and 연산자
//println( 3.14 and 5.55 ) //ERROR - Double 타입은 비트연산 안됨
//println( "aaa" or "bbb" ) //ERROR - String 타입은 비트연산 안됨
4. 조건문 - 코틀린에서 달라진 것들
코틀린의 조건문 종류 : if, when [ switch 문법이 없음 ]
1) 삼항연산자를 대체하는 if 문법
var str= (10>5)? "Hello" : "Nice" //ERROR : 삼항연산자 문법이 없음
위 코드는 에러입니다. 코틀린은 삼항(조건) 연산자가 없습니다. 대신 if 문법이 이를 대체합니다.
var str= if(10>5) "Hello" else "Nice" //참이면 "Hello" 거짓이면 "Nice"
println(str) //결과: Hello
삼항연산자의 ? : 이라는 기호를 쓰지않고 영문자인 if - else 문을 사용하여 보다 가독성이 좋아진 것 같습니다.
2)혹시 if나 else문의 실행문이 여러줄이면 마지막 실행문 값이 변수에 대입됨
str= if(10<5){
"zzzzz"
"aaaaa" //참이면 이 문자열이 str에 대입됨
}else{
"qqqqq"
println("ggggg") //당연히 이렇게 출력문 같은 코드가 있어도 됨.
"bbbbb" //거짓이면 이 문자열이 str에 대입됨
}
println(str)
//**출력**
ggggg <-- else{} 영역안에 있는 println("ggggg") 실행문에 의한 출력
bbbbb <-- 조건문 밖의 println(str) 실행문의 의한 출력
str변수의 자료형이 String 이기에 조건문 영역의 마지막 실행문 값이 문자열이 아니면 에러가 발생합니다.
str= if(10>5){
"zzzzz"
"aaaaa"
}else{
"qqqqq"
println("ggggg") //당연히 이렇게 출력문 같은 코드가 있어도 됨.
"bbbbb"
println("ggggg") //ERROR - 마지막이 있으면 당연히 에러 [ str의 자료형이 String이어서 에러임. Any였다면 에러 아님. 참고로 println()함수의 자료형은 Kotlin.Unit 타입임]
}
3) if 표현식 - 연산식처럼 특정 결과값을 준다고 하여 if 표현식 이라고 부름
위 코드처럼 if문이 마치 3+5 라는 연산식의 결과값 8을 주듯이 특정 결과값을 준다고 하여 코틀린에서는 if문 대신에 if표현식이라고 부릅니다.
4) switch 문법이 없어지고 when 문법이 이를 대체
var h:Any?= null
//문법이 없어서 switch를 쓰면 에러
//switch(h){ } //ERROR
h=10
when(h){ //h값으로 분기
10-> println("aaa") //h가 10일때 실행
20-> println("bbb") //h가 20일때 실행
}
분기되는 값들의 자료형이 달라도 에러가 아닙니다.(자바와 다름)
h=10
when(h){
10-> println("aaa")
20-> println("bbb")
//자료형이 달라도 상관없음
"Hello"-> println("Hello")
true-> println("true")
}
분기되는 위치에 변수가 있어도 됩니다.
var nn:Int= 100 //정수형 변수 nn 선언
h=10
when(h){
10-> println("aaa")
20-> println("bbb")
//자료형이 달라도 상관없음
"Hello"-> println("Hello")
true-> println("true")
//변수가 있어도 됨 (첫줄에 있는 변수 nn이 100을 가지고 있음)
nn-> println("100을 가지고 있습니다.")
}
분기되는 위치에 2개 이상의 값을 묶을 수도 있습니다.
var nn:Int= 100 //정수형 변수 nn 선언
h=10
when(h){
10-> println("aaa")
20-> println("bbb")
//자료형이 달라도 상관없음
"Hello"-> println("Hello")
true-> println("true")
//변수가 있어도 됨 (첫줄에 있는 변수 nn이 100을 가지고 있음)
nn-> println("100을 가지고 있습니다.")
//2개이상의 조건을 묶을 수 있음
30,40-> println("30 or 40 입니다.")
}
어떤 조건값에도 해당하지 않을 때에 사용하는 switch 문의 default 역할은 else 키워드로 적용할 수 있습니다. 실행문을 여러줄 사용하고 싶다면 { .. } 영역표시를 하면 됩니다.
var nn:Int= 100 //정수형 변수 nn 선언
h=10
when(h){
10-> println("aaa")
20-> println("bbb")
//자료형이 달라도 상관없음
"Hello"-> println("Hello")
true-> println("true")
//변수가 있어도 됨 (첫줄에 있는 변수 nn이 100을 가지고 있음)
nn-> println("100을 가지고 있습니다.")
//2개이상의 조건을 묶을 수 있음
30,40-> println("30 or 40 입니다.")
//switch문의 default역할 : 실행할 코드가 여러줄이면 {}로 묶어서...
else->{
println("ccc")
println("end")
}
}
when도 if문처럼 표현식이라서 결과를 변수에 저장하는 것이 가능함
h=20
var result= when(h){
10->"Hello"
20->"Nice"
else->{
println("else")
"BAD"
}
}
println(result) //출력: Nice
when에 is 연산자 키워드 사용도 가능합니다.
when(h){
is Int -> println("Int 타입입니다.") //h의 자료형이 Int면 분기되어 실행
is String -> println("String 타입입니다.") //h의 자료형이 String이면 분기되어 실행
else -> println("else") //Int, String 이 아닐때 실행
}
when을 특정 수식으로 제어하고 싶을 때 사용하는 방법도 있습니다. 주의할 것은 when()에서 ()를 생략하며 분기 대상을 미리 지정하지 않는다는 겁니다.
h=85
when{ //()가 생략되어 있음!!
h>=90 && h<=1000-> println("A학점 입니다")
h>=80 -> println("B학점 입니다.")
h>=70 -> println("C학점 입니다.")
h>=60 -> println("D학점 입니다.")
else -> println("F학점 낙제 입니다.")
}
range 문법( 점이 3개)으로 위 분기조건 중 && 연산 범위를 간략하게 표현할 수 있습니다. [range ...(점3개) 문법은 반복문을 통해 소개]
h=85
when{
//h>=90 && h<=100-> println("A학점 입니다")
h in 90..100 -> println("A학점 입니다") // 90..100 == 90~100 범위
h>=80 -> println("B학점 입니다.")
h>=70 -> println("C학점 입니다.")
h>=60 -> println("D학점 입니다.")
else -> println("F학점 낙제 입니다.")
}
when에 in키워드 연산자를 통해 Collections의 요소들값을 체크할 수도 있습니다. 이는 for문과 배열에 대한 소개 후 소개하도록 하겠습니다.
5. 반복문 - 코틀린에서 달라진 것들
코틀린의 반목문 종류 : while, for
1) while 은 특별히 다른점이 없습니다. if, when 처럼 마지막 값을 주는 표현식도 아닙니다. 기존에 사용하던 방식 그래도 사용하면 됩니다.
2) for문은 작성하는 방법이 완전히 다릅니다. 기존 자바문법을 그대로 사용하면 에러입니다.
for(var i=0; i<5; i++){} //ERROR - 이런 문법이 없음
코틀린의 반복문의 횟수를 지정하는 방법을 살펴보겠습니다. [ 문법 : for( 제어변수 in 범위 ) { .. } ]
3) 0부터 5까지 6번 실행되는 반복문을 만들어 보겠습니다.
for(i in 0..5){ //i변수 앞에 var쓰면 에러
println(i)
}
//**출력**
0
1
2
3
4
5
4) 3~10 까지 반복하기. - for안의 제어변수 i 는 지역변수이기에 위에 사용했던 변수명을 사용해도 문제없습니다.
for(i in 3 .. 10){ //..양옆의 공백은 상관없음 - 3~10
println(i)
}
//**출력**
3
4
5
6
7
8
9
10
근데 자바와 for문에 익숙하시다면 알겠지만 마지막 숫자를 반복에 사용하지 않는 경우가 많지요. 이 때는 range .. 연산자를 사용하지 않습니다.
5) 마지막 숫자 전까지 반복하기 [ .. 대신에 until ]
for(i in 0 until 10){ // 0 ~ 9
println(i)
}
//**출력**
0
1
2
3
4
5
6
7
8
9
6) 반복 할 때마다 2씩 증가하기 [ step ]
for(i in 0..10 step 2){ //0~10까지 2씩 증가
println(i)
}
//**출력**
0
2
4
6
8
10
7) 반복 할 때 마다 값을 감소 [ downTo ]
for(i in 10 downTo 0) println(i) //10~0까지 1씩 감소
//**출력**
10
9
8
7
6
5
4
3
2
1
0
8) 반복 할 때 마다 값을 2씩 감소 [ downTo + step ]
for(i in 10 downTo 0 step 2) println(i) //10~0까지 2씩 감소
//**출력**
10
8
6
4
2
0
♣ 기타 제어문 - break, continue
기본적인 break, continue 의 사용문법은 자바와 차이가 없습니다.
1) 0부터 5까지 반복 중에 3이 되면 반복문 종료(break)
for (n in 0..5){
if(n==3) break
print("$n ") //반복할 때 마다 제어변수 n 출력 - 줄바꿈 안함
}
//**출력**
0 1 2
2) 중첩 반복문의 안쪽 반복문에서 break 사용시 안쪽 반복문만 종료
for(y in 0..5){ //0~5까지 6번 반복
print("$y : ") //제어변수 y 값 출력 [ 문자열 탬플릿 : 문자열 " "안에서 $y 로 변수 인식 ]
for(x in 0..10){ //0~10까지 11번 반복
if(x==6) break //6이 되면 안쪽 반복문만 멈춤
print("$x ") //제어변수 y 값 출력
}
println()
}
//**출력**
0 : 0 1 2 3 4 5
1 : 0 1 2 3 4 5
2 : 0 1 2 3 4 5
3 : 0 1 2 3 4 5
4 : 0 1 2 3 4 5
5 : 0 1 2 3 4 5
바깥쪽 for 문 y는 0부터 5까지 변함없이 반복하고 안쪽 for문만 종료되는 것을 볼 수 있습니다.
근데 경우에 따라 안쪽 for 문의 특정 조건이 되었을 때 바깥쪽 for 문까지 종료하여야 하는 경우도 필요할 수 있습니다. 코틀린은 이런 때 사용하기 위해 종료하는 반복문의 위치를 식별할 수 있도록 표식을 남길 수 있습니다. 이를 Label 이라고 부릅니다.
3) 반복 종료 지점을 표시하는 Label : @로 종료위치 선택
KKK@ for(y in 0..5){ //for의 지점에 KKK 라벨 지정
print("$y : ")
for(x in 0..10){
if(x==6) break@KKK //"KKK" Label의 for문을 break
print("$x ")
}
println()
}
//**출력**
0 : 0 1 2 3 4 5 //바깥쪽 KKK반복문이 종료되어 전체 반복 종료
Label 을 소개해 드리긴 했지만 개발자들은 이를 반복문에서는 잘 사용하지 않습니다. 코드의 순서 흐름을 따라가기 혼란스러워 선호하지 않는 편이고 이런 상황에서는 함수나 return 을 이용하여 해당 기능을 구현하는 것이 일반적입니다.
그럼 Label은 굳이 익힐 필요가 없겠네요? 그렇지 않습니다.
@로 표시하는 라벨은 컴파일러에게 해당 키워드가 누구인가를 지정할 때 매우 많이 사용합니다. 대표적으로 이너클래스에서 아웃터클래스를 지칭할 때 사용던 아웃터클래스명.this 를 this@아웃터클래스명 으로 사용합니다. 코틀린 앱 개발 코딩 중에 등장할 때 다시금 살펴보고 지금은 Label 이라는 문법이 특정 위치나 대상을 지칭하는데 사용하는 문법이라는 것만 대략적으로 기억해 두시기 바랍니다.
코틀린 for 문법의 또 다른 사용법은 배열이나 컬렉션을 진행하면서 추가로 소개하도록 하겠습니다.
'소소한 소스코드' 카테고리의 다른 글
[안드로이드 Android] Kotlin 언어로의 전환 5. 함수 function - 기본문법, 함수선언 단순화, 익명함수, 고차함수 (0) | 2025.02.23 |
---|---|
[안드로이드 Android] Kotlin 언어로의 전환 4. 배열 Array & 컬렉션 Collection (0) | 2025.02.19 |
[안드로이드 Android] Kotlin 문자열 format 만들기 (0) | 2025.02.14 |
[안드로이드 Android] Kotlin 언어로의 전환 2. 자료형과 변수 (0) | 2025.02.14 |
[안드로이드 Android] Kotlin 언어로의 전환 1. 주요특징 및 화면출력 (0) | 2025.02.08 |