반응형
본 포스트는 지인들과 스터디한 내용을 정리한 포스트입니다
http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9788931463422
1. 함수를 사용하자
- 재사용 가능한 가장 작은 단위가 클래스인 Java와 달리, 코틀린에서는 클래스는 물론 단독함수까지 모두 재사용이 가능하다
- 함수를 호출할때 모든 파라미터를 전달하지 않고 기본 파라미터를 전달할 수 있다 ( 함수와 메서드를 쉽게 확장할 수 있다 )
- 메소드 사용을 효율적으로 만들기 위해서 코틀린은 아규먼트에 이름을 만들 수 있도록 허용
2. 함수 생성
- 키스 함수(KISS, Keep It Simple, Stupid. "단순하게 해, 멍청아!")
- 예제 코드: 가장 단순하고 짧은 함수 정의
// kiss.kts fun greet() = "Hello" println(greet())
- 함수의 정의는 fun키워드로 시작한다
- 함수 이름("greet") 다음에는 파라미터 리스트가 나온다
- 함수가 매우 짧은 단일표현함수라면 위 예제처럼 함수 바디를 {}로 만드는 대신 =을 이용하여 만들 수 있다 ( 리턴타입 추론까지 가능, return 키워드를 사용할 수 없음 )
- 예제 코드: 가장 단순하고 짧은 함수 정의
- 리턴타입과 타입추론
- 코틀린은 {} 블록 바디가 없는 함수(단일표현함수)에 대해 타입 추론을 해준다, 즉 return 할 필요도, 리턴타입을 명시해줄 필요도 없다
- 전에 설명했던 대로 타입 추론은 컴파일 시점에서 일어난다, 미스 매치된 타입을 할당하려고하면 컴파일 오류가 난다
- 하지만 함수가 외부에서 사용되거나 복잡하다면 리턴타입을 지정해주도록 하자!! ( 명확하게 설명할 수 있으며, 코드 구현부에 의해 결정되는 타입추론이 의도치 않은 다른 타입으로 변경되는 상황을 방지할 수 있다 )
- 리턴타입은 파라미터 리스트 뒤에 ':' 키워드를 붙여서 작성한다
- return 키워드는 단일표현식 함수이고, 바디가 블록이 아니라면 사용할 수 없다 ( 리턴타입이 명확하다면 타입추론 사용, 명확하지 않다면 리턴타입을 명시해주자 )
- 모든 함수는 표현식이다
- 코틀린은 Unit이라는 특별한 타입을 사용, Unit은 Java의 void형과 대응된다
- 리턴할 게 아무것도 없는 경우 Unit을 사용할 수 있다
- 코틀린은 함수에 리턴이 없으면 Unit타입을 리턴 타입으로 추론한다
- 예제 코드
// inferunit.kts fun sayHello() = println("Well, hello") val message: String = sayHello() // ERROR, Return Type이 Unit //error: type mismatch: inferred type is Unit but String was expected (inferunit.kts:2:23) //inferunit.kts:2:23: error: type mismatch: inferred type is Unit but String was expected //val message: String = sayHello() // ERROR, Return Type이 Unit
- 타입 추론을 명시적으로 Unit으로 줘도 가능하다
- 심지어 Unit 타입 변수에 할당도 가능
- Unit 타입은 toString(), equals(), hashCode() 메소드를 가지고 있다
- 모든 함수는 유용한 리턴을 준다, 혹은 리턴이 없다면 Unit을 리턴한다. 따라서 모든 함수는 표현식으로 취급될 수 있고 그 결과들은 변수에 할당되거나 추후 프로세스에 사용될 수 있다
- 파라미터 정의하기
- 코틀린은 함수나 메소드에 파라미터의 타입을 명시하도록 한다
- 파라미터의 타입을 파라미터 이름 바로 뒤에 ':'으로 구분해서 명시해준다
- 예제 코드
// passingarguments.kts fun greet(name: String): String = "Hello $name" println(greet("Eve"))
- 코틀린은 파라미터를 val나 var로 명시적인 지정을 할 수 없게 만들었고 ( 전부다 val, 이뮤터블 ) 함수나 메소드에서 파라미터의 값을 변화시키려고 하면 컴파일 오류를 발생시킨다
- 블록바디로 만든 함수
- 함수가 복잡하다면 {} 블록을 사용해서 바디를 만들면 된다
- {} 블록 바디를 이용하면 반드시 리턴타입을 정의해줘야한다 ( 정의하지 않으면 Unit으로 타입 추론된다 )
- 예제 코드
// multilinefunction.kts fun max(numbers: IntArray): Int { var large = Int.MIN_VALUE for (number in numbers) { large = if (number > large) number else large } return large } println(max(intArrayOf(1,5,2,12,7,3))) // 12
- 코틀린은 코드 블록 안으로 들어가서 리턴타입을 추론하지 않는다
- 특정 리턴타입을 명시하고 =을 사용한뒤 {} 블록 바디를 사용하면 컴파일 오류가 난다
- 반드시 단일 표현식에만 '='을 사용하고 복잡한 함수 + 리턴타입 명시를 할때만 {} 블록을 사용하자
- 예제 코드
// caveat.kts fun f1() = 2 fun f2() = { 2 } println(f1()) // 2 println(f2()) // () -> kotlin.Int
- f1은 {} 블록없이 단일표현식으로 타입추론이 되어 의도한 대로 리턴값이 나왔다
- f2는 =에 {} 블록을 감싼뒤 2를 적었다, 코틀린은 해당 코드를 람다표현식으로 추론한다 ( () -> kotlin.Int )
3. 기본 인자와 명시적 인자
- 코틀린도 Java처럼 오버로딩이 가능하다
- 기본 아규먼트 기능이 더 단순하고, 함수를 변경하는 좋은 방법이긴 하나 바이너리가 변경되므로 컴파일을 다시 해야한다
- 명시적 아규먼트는 읽기 좋은 코드를 만드는 아주 유용한 방법이다
- 기본 아규먼트를 통한 함수 변경
- 기존의 Java에서는 원형 함수에 확장성이 필요하여 수정하려할 경우 원형 함수는 그대로 놔두고 아규먼트 갯수를 조절하여 오버로딩을 통해 해결했다 ( 하지만 이와 같은 방식은 코드의 중복이 발생한다 )
- 코틀린은 위의 문제를 기본 아규먼트를 이용해서 해결했다
- 예제 코드
// defaultarguments.kts fun greet(name: String, msg: String = "Hello"): String = "$msg $name" println(greet("Eve")) // Hello Eve println(greet("Eve", "Howdy")) // Howdy Eve
- greet 함수는 String type의 "name" 파라미터와 String type의 "msg" 파라미터를 가지고 있다
- 하지만 "msg" 파라미터가 선언과 동시에 초기화도 되어 있는 것을 확인할 수 있는데 이는 인자값이 들어오지 않았을 때 default 값으로 바인딩된다
- 기본 아규먼트를 효과적으로 만들기 위해서는 맨 마지막에 사용하는 것이 좋고, 람다표현식을 파라미터로 사용할 경우 바로 그 앞까지 사용하면 된다
- 명시적 아규먼트를 이용한 가독성 향상
- 함수에 대한 파라미터들의 의미를 알려면 문서나 함수 정의 부분을 참고해야하는 어려움이 있다
- 이를 명시적인 파라미터 전달을 통해 해결할 수 있다
- 비교 코드
// namedarguments.kts fun createPerson(name: String, height: Int, weight: Int, age: Int = 1) { println("$name $age $height $weight") } createPerson("jimin.joo", 173, 70, 31) // original createPerson(name = "jimin.joo", height = 173, weight = 70, age = 31) // named
- 물론.. intelliJ가 각 아규먼트에 대한 정보를 보여주긴 하지만 명시적으로 나타내어 가독성을 향상시킬 수 있다
- 파라미터들이 어떤 의미인지 추측할 필요도 없다
- 또한 명시적인 아규먼트는 파라미터 순서와 상관없이 사용할 수 있다 ( 직접 명시해주니까! )
- 이런 방식은 함수에 발생할 수 있는 잠재적인 오류의 가능성들을 제거해준다
4. 다중인자와 스프레드
- 코틀린의 다중인자(vararg,, 뭐라고 읽는거지..) 기능은 함수가 한 번에 여러 개의 인자를 받을 때 타입 안정성을 제공해주는 기능
- 스프레드 연산자는 콜렉션의 값을 개별 값으로 분해하거나 나열할 때 유용하다
- 여러개의 인자
- 코틀린 함수들은 많은 수의 인자를 받을 수 있다
- 예제 코드
// vararg.kts fun max(vararg numbers: Int): Int { var large = Int.MIN_VALUE for (number in numbers) { large = if ( number > large ) number else large } return large } println(max(1,4,3,6,2))
- 기존에 IntArray 타입이 였던 파라미터가 vararg 키워드로 선언됨과 동시에 Int 타입으로 변경되었다
- 즉 vararg 키워드가 파라미터로 특정 타입(Int)의 배열이 들어갈 수도 있다는 점을 알려준다
- vararg는 마지막에 두어서 호출 시 명시적 인자를 필수적으로 사용할 필요가 없도록 만들자
- 역시나 마지막 파라미터가 람다표현식일 경우 마지막 바로 전에 둔다
- 스프레드 연산자
- 함수가 다중 인자를 받을 수 있도록 정의되어 있어도 배열이나 리스트를 직접 받을 수는 없다
- 이럴때 스프레드 연산자가 필요하다
- 즉 파라미터가 vararg라고 작성되어있는 경우, 스프레드 연산자 *을 이용해서 배열을 넘길 수 있다 ( 당연히 같은 타입만 가능하다 )
- 예제 코드
fun max(vararg numbers: Int): Int { var large = Int.MIN_VALUE for (number in numbers) { large = if (number > large) number else large } return large } val values = intArrayOf(1, 2, 3, 4, 5) println(max(values)) // compile error println(max(*values))
- 배열이 있으면 스프레드를 사용할 수 있지만 리스트는 스프레드 연산자를 사용할 수 없다
- 이럴 땐 리스트를 배열로 변환하여 적용하면 된다
*listOf(1, 4, 8, 12).toIntArray()
5. 구조분해
- 구조화란 다른 변수의 값으로 객체를 만드는 것, 구조 분해는 이미 존재하는 객체에 값을 추출해 변수로 넣는 작업이다
- 코틀린의 구조분해는 속성의 이름이 아닌 속성의 위치를 기반으로 진행된다
- 예제 코드 ( 구조분해 )
val (first, middle, last) = getFullName() println("$first $middle $last")
- 객체의 소성들이 구조분해되는 순서는 객체의 컨스트럭터가 객체를 초기화할 때 속성을 만드는 순서와 동일하다
- 필요없는 속성은 '_'를 이용하여 스킵할 수 있다
- 특정 포지션에서 할당을 멈추고, 이후 값을 무시하고 싶으면 아무것도 적지 않으면 된다
728x90
반응형
'Study > 다재다능 코틀린 프로그래밍' 카테고리의 다른 글
Day 6. 오류를 예방하는 타입 안정성 (0) | 2021.11.02 |
---|---|
Day 5. 컬렉션 사용하기 (0) | 2021.10.31 |
Day 4. 외부 반복과 아규먼트 매칭 (0) | 2021.10.27 |
Day 2. Java 개발자를 위한 코틀린 필수 사항 (0) | 2021.10.21 |
Day 1. 코틀린 시작하기 (0) | 2021.10.19 |