2022. 9. 10. 17:00ㆍSpring/Kotlin
kotlin의 기본 문법을 정리하고 용례를 확인하는 것이 해당 포스팅의 목표입니다.
안녕하세요. 이번 포스팅에서는 Java와 비교하여 kotlin 문법을 익히도록 합니다.
추석을 맞아 코틀린 도장깨기를 하려했는데 생각보다 시간이 많이 걸리네요 🥲
이번 달 내로 코프링 데모 프로젝트를 만들어보고 싶어 시작했습니다 💪🏻
fun
: 함수 및 메서드 선언 방식
- 최상위 수준 정의 가능
📌 vs.Java : 자바와 다르게 클래스 안에 넣어야 할 필요가 없음
fun max(a: Int, b: Int) : Int {
return if (a > b) a else b
}
if
코틀린 if는 값을 만들어내지 못하는 문장statement이 아니고 결과를 만드는 식 expression
삼항연산자와 비슷 (e.g. (a > b) ? a : b
)
// 바로 Return 가능 (fyi. kotlin의 if 절은 식(expression)이기 때문에 값 return)
fun ex1_max(a: Int, b: Int): Int = if (a > b) a else b
// Return Type 생략 가능
fun ex2_max(a: Int, b: Int) = if (a > b) a else b
ex1: if 문에서 return a 혹은 return b가 아닌 a, b 를 직접 return하는데 코틀린에서 if 절은 식expression이기 때문
💡 Expression VS Statement
expression (식): 값을 만들어 내며 다른 식의 하위 요소로 계산에 참여할 수 있음
statement (문): 자신을 둘러싸고 있는 가장 안쪽 블록의 최상위 요소로 존재하며 아무런 값을 만들어내지 않음
- 중괄호로 둘러싸인 함수 : 블록이 본문인 함수 expression body
- 등호와 식으로 이뤄진 함수: 식이 본문인 함수 block body
코틀린에서는 block body가 많이 사용됨
println
: 콘솔 출력
📌 vs.Java: System.out.printin 대신 사용
코틀린 표준 라이브러리는 여러 가지 표준 자바 라이브러리 함수를 간결하게 사용할 수 있게 감싼 래퍼 wrapper를 제공하는데 println도 그 중 하나
Variable
✔️ val
value. 변경 불가능한 immutable 참조를 저장하는 변수
- val로 선언된 변수는 일단 초기화하고 나면 재할당이 불가능
📌 vs.Java: final 변수에 해당
✔️ var
variable. 변경 가능한 mutable 참조. 값이 바뀔 수 있음
📌 vs.Java: 일반 변수에 해당
// ==== val ====
val gngsn = "변수 지정" // 타입 추론: String
val yearsToCompute = 7.5e6 // 타입 추론: Double (부동소수점floating point 상수)
val answer = 42 // 타입 추론: Int
// or 타입 직접 지정
val answer:Int = 42
// answert = 31 <-- ERROR: Val cannot be reassigned
// ==== var ====
var message = "코틀린을 언제 다 뗄 수 있을까 ~.~"
message = if (Random(3).nextInt() > 10) "아마 10일 뒤.." else "지금은 아님"
타입을 지정하지 않으면 컴파일러가 초기화 식을 분석해서 초기화 식의 타입을 변수 타입으로 지정
💡 기본적으로는 모든 변수를 val 키워드를 사용해 불변 변수로 선언하고, 나중에 꼭 필요 할 때에만 var로 변경하라
변경 불가능한 참조와 변경 불가능한 객체를 부수 효과가 없는 함수와 조합해 사용하면 코드가 함수형 코드에 가까워진다
String Template
: 문자열 템플릿. 문자열에 편리한 변수 출력
fun main(args: Array<String>) {
val name = if (args.size > 0)
printin("Hello, $name!") args [0] else "Kotlin"
}
$variable
과 ${variable}
은 동일한데, 확실한 명시를 위해 중괄호 ${} 로 둘러싼 형태를 쓰도록 하자
중괄호로 둘러싼 식 안에서 문자열 템플릿을 사용 가능
예를 들어 ${if (s.length > 2) short" else "normal string ${s}"}
와 같은 문자열도 사용할 수 있다.
📌 vs.Java: 자바의 "Hello, " + name + "!" 와 코틀린의 "Hello, $name!" 과 동일
Class
코드없이 데이터만 저장하는 클래스를 Value object, VO라 부르는데,
코틀린을 비롯한 다양한 언어가 위와 같이 간결하게 기술할 수 있는 구문을 제공
- 프로퍼티를 소괄호 내에 정의하면서 name을 파라미터로 받는 생성자 생성
- 위의 정의로 자바의 Person(String name) {}
에 해당하는 기본적인 생성자가 됨
- 추가적인 생성자 정의는 constructor(...) {}
를 따로 정의할 수 있음
접근제한자: 기본 가시성은 public이며 생략 가능
Property
📌 vs.Java
클래스의 목적은 데이터를 캡슐화encapsulate이며, 자바에서 이를 위해 프로퍼티property를 사용
프로퍼티property : 필드 + 접근자
1# 필드 field
: 자바에서는 데이터를 보통 비공개Private 필드field에 저장
2# 접근자 메소드 accessor method
- 게터getter: 필드 읽기
- 세터setter: 필드 변경
코틀린 프로퍼티는 자바의 필드와 접근자 메소드를 동일한 개념으로 사용
1# 필드 field
프로퍼티를 선언할 때는 변수 선언과 동일하게 val이나 var를 사용
2# 접근자 메소드 accessor method
기본적으로 자동 생성
val name : 읽기 전용 프로퍼티. 비공개 필드, 공개 게터 private field, public getter 생성
var isMarried: 쓸 수 있는 프로퍼티. 비공개 필드, 공개 게터, 공개 세터 private field, public getter, public setter 생성
코틀린에서는 getter와 setter를 필드에 직접 접근하는 것처럼 사용
val person = Person("gngsn", false)
println(person.name) // Getter: gngsn
println(person.isMarried) // Getter: fasle
person.isMarried = false; // Setter
Custom
아래처럼 get()
으로 custom한 프로퍼티 생성 가능
Rectangle(val height: Int, val width:Int) {
val isSquare: Boolean
get() = height == width
}
fun test_properties() {
val rect = Rectangle(200, 200)
println(rect.isSquare)
// rect.isSquare = true !ERROR
}
enum
열거형
enum 은 소프트 키워드soft keyword
class 는 키워드 keyword
enum은 class 앞에 있을 때 특별한 의미(다른 곳에서는 이름으로 사용 가능)
코틀린에서 유일하게 세미콜론이 필수
// ex1
enum class Color {
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}
// ex2
enum class Color(
val r: Int, val g: Int, val b: Int // 상수 프로퍼티 정의
) {
RED(255, 0, 0), ORANGE(255, 165, 0),
YELLOW(255, 255, 0), GREEN(0, 255, 0), BLUE(0, 0, 255),
INDIGO(75, 0, 130), VIOLET(238, 130, 238); // 반드시 세미콜론 표시
fun rgb() = (r * 256 + g) * 256 + b // enum 클래스 내 메소드를 정의
}
when
Java의 switch 문을 대치하지만 훨씬 강력
- if와 마찬가지로 when도 값 생성
- 식이 본문인 함수에 when 을 바로 사용할 수 있음
- 각 분기의 끝에 break를 넣지 않아도 됨
오른 쪽은 enum class Color 의 모든 필드를 모두 불러와서 코드를 깔끔하게 만들 수 있음
한 분기 안에서 여러 값을 콤마(,)로 연결해 매치 패턴으로 사용할 수도 있음
Smart Cast
타입 검사 이후 타입을 명시하지 않아도 컴파일러가 캐스팅을 수행
interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr
is
는 자바의 instanceof
와 비슷하지만 명시적으로 변수 타입을 캐스팅하지 않아도 스마트 캐스트 발생
fun eval(e: Expr): Int {
if (e is Num) {
val n = e as Num // 캐스트 타입을 직접 명시 (스마트 캐스트 사용 X)
return n.value
}
if (e is Sum) { // 변수 e에 대해 스마트 캐스트를 사용
return eval(e.right) + eval(e.left)
}
throw IllegalArgumentException("Unknown expression")
}
while
코틀린 while 루프는 자바와 동일
while (condition) {
/* ... */
}
do {
/* ... */
} while (condition)
for
for <item> in <elements>
for는 자바의 for-each 루프에 해당하는 형태만 존재
초깃값, 증가 값, 최종 값을 사용한 루프를 대신하기 위해 코틀린에서는 범위를 사용해서 수열을 생성
수열progression: 일정한 순서로 이터레이션하는 경우
val oneToTen = 1. . 10
// 순차적인 수를 세면서 3으로 나눠떨어지는 수는 Fizz, 5로 나눠떨어지는 수는 Buzz라고 말해야 하며
// 어떤 수가 3과 5로 모두 나눠떨어지면 'FizzBuzz'라고 말하는 게임
fun fizzBuzz(i: Int) = when {
i % 15 == 0 -> "FizzBuzz "
i % 3 == 0 -> "Fizz "
i % 5 == 0 -> "Buzz "
else -> "$i "
}
>> for (i in 1..100) println(fizzBuzz(i))
// 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz ...
// 100 downTo 1은 역방향 수열을 만든다 역방향 수열의 기본 증가 값은 -1
>> for (i in 100 downTo 1 step 2) print(fizzBuzz(i))
// Buzz 98 Fizz 94 92 FizzBuzz 88 86 Fizz 82 Buzz Fizz 76 74 Fizz Buzz 68 Fizz 64 62 FizzBuzz ...
downTo : 역방향 수열 생성. 기본 증가 값은 -1
Map iteration
val binaryReps = TreeMap<Char, String>()
for (c in 'A'..'F') {
val binary = Integer.toBinaryString(c.code)
binaryReps[c] = binary // Java에서 binary.put(c, binary) 와 동일
}
for ((letter, binary) in binaryReps) {
println("$letter = $binary")
}
// A = 1000001 | B = 1000010 | C = 1000011 | D = 1000100 | E = 1000101 | F = 1000110 |
withIndex
인덱스와 함께 컬렉션을 이터레이션
val list = arrayListOf ("10", "11", "1001")
for ((index, element) in list.withIndex()) {
print("[$index] $element, ")
}
// [0] 10, [1] 11, [2] 1001,
in
: 어떤 값이 범위에 속하는지 검사
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
isLetter('q') // true
!in
어떤 값이 범위에 속하지 않는지 검사
fun isNotDigit(c: Char) = c !in '0'..'9' // '0'<= c && x<= '9' 로 변환
isNotDigit('x') // true
when과 함께 사용 가능
fun recognize(c: Char) = when (c) {
in '0'..'9' -> "It's a digit!"
in 'a'..'z', in 'A'..'Z' -> "It's a letter!"
else -> "I don't know..."
}
recognize('8') // "It's a digit!"
recognize('x') // "It's a letter!"
java.lang.Comparable
인터페이스를 구현한 클래스라면 비교가 가능하기 때문에
그 클래스의 인스턴스 객체를 사용해 범위를 만들 수 있음
println("Kotlin" in "Java".."Scala") // true
println("Kotlin" in setOf("Java", "Scala") // false
"Kotlin" in "Java".."Scala"
은 코틀린이 "Java" <= "Kotlin" && "Kotlin" <= "Scala"
로 변경
Exception
코틀린의 예외exception 처리는 자바나 다른 언어의 예외 처리와 비슷
함수는 정상적으로 종료할 수 있지만 오류가 발생하면 예외를 던질throw 수 있음
- 자바 코드와 가장 큰 차이는 throws 절이 코드에 없다는 점
: 자바에서는 체크 예외를 명시적으로 처리하지만,
코틀린은 체크 예외와 언체크 예외unchecked exception를 구별하지 않음
코틀린에서는 함수가 던지는 예외를 지정하지 않고 발생한 예외를 잡아내도 되고 잡아내지 않아도 됨
try, catch, finally
- 발생한 예외를 함수 호출 단에서 처리catch하지 않으면 함수 호출 스택을 거슬러 올라가면서
예외를 처리하는 부분이 나올 때까지 예외를 다시 던짐
fun readNumber(reader:BufferedReader): Int? {
return try {
Integer.parseInt(reader.readLine()) // return 값
} catch (e: NumberFormatException) {
null
} finally {
reader.close()
}
}
위의 코드처럼 try도 식expression이 될 수 있음
그럼 지금까지 Kotlin 기본 문법에 대해 알아보았습니다.
오타나 잘못된 내용을 댓글로 남겨주세요!
감사합니다 ☺️
| Ref |
'Spring > Kotlin' 카테고리의 다른 글
Kotlin, 어렵지 않게 사용하기 (4) - Object 2 (0) | 2022.09.22 |
---|---|
Kotlin, 어렵지 않게 사용하기 (3) - Object 1 (0) | 2022.09.20 |
Kotlin, 코루틴 제대로 이해하기 - (2) (0) | 2022.09.13 |
Kotlin, 코루틴 제대로 이해하기 - (1) (2) | 2022.09.12 |
Kotlin, 어렵지 않게 사용하기 (2) - 함수 (0) | 2022.09.11 |
Backend Software Engineer
𝐒𝐮𝐧 · 𝙂𝙮𝙚𝙤𝙣𝙜𝙨𝙪𝙣 𝙋𝙖𝙧𝙠