2장 코틀린 기초
2.1 기본요소
- 함수
반환값이 없는 경우와 함수의 기본
fun main(args: Array<String>, i: Int) {
println("Hello, world!!!")
}
>>> Hello, world!!!
// 일반적인 함수본문
fun main() {
println(max(1,2))
}
fun max(a: Int, b: Int): Int {
return if (a > b) a else b
}
>>> 2
// 식을 함수본문으로 변환사용 한경우
fun main() {
println(max(1,2))
}
fun max(a: Int, b: Int): Int = if (a > b) a else b
>>> 2
// 반환 타입 생략
fun main() {
println(max(1,2))
}
fun max(a: Int, b: Int): Int = if (a > b) a else b
>>> 2
- 함수 선언은 fun 키워드를 사용한다
- 파라미터 이름뒤에 파리미터의 타입을 쓴다
- 변수도 동일
- args : 파라미터 이름
- Array<String> : 파라미터 타입
- 파이썬처럼 함수를 최상위 수준에 정의할 수 있다.
- 배열처리를 위한 문법이 존재하지 않는다
- Array<String> 바로 사용가능
- println 같은 자바 라이브러리를 간결할게 사용할 수 있는 래퍼를 제공
- 세미콜론 없음!!!
- 반환값 없는 함수에 void 선언 안해도 된다
- 코틀린에서는 식(expression)이 본문(statement)인 함수가 많이 쓰인다
- 인텔이제이에서 변환 지원 (🍯🍯🍯 )
- 식을 본문으로 사용할경우 컴파일러가 타입추론으로 반환 타입을 자동으로 분석반환한다.
- 주의 : 반대로 블록이 본문인경우는 꼭 반환타입을 써줘야한다
- 변수
// 타입 미지정, 초기화 변수 선언
val question = "삶, 우주, 그리고 모든것에 대한 궁극적인 질문"
val answer = 42
// 타입 지정, 초기화 변수 선언
val answer: Int = 42
// 초기화 하지 않는경우 타입 선언 필수
val answer: Int
// 부동소수점의 경우 Double 타입으로 컴파일러가 인식
val yearsToComputer = 7.5e6
// 값을 초기화 하지 않음
val message: String
// 조건에 따른 val 값의 초기화, 이후 변수 값 변경 불가
if (canPerFormOperation()) {
message = "success"
} else {
message = "fail"
}
// val 객체 내부값은 변경 가능하다
val languages = arrayListOf("java")
languages.add("kotlin")
// var 같은 타입의 값으로만 변경 가능하다
// 아래의 경우 type mismatch 에러발생
var message = "success"
message = 0
- val
- val로 지정된 불변 타입 변수는 초기에 값을 할당되면 나중에 값을 변경할 수 없으며 값을 변경하게 되면 컴파일 에러가 발생
- Java 의 final
- 블록 실행시 한번만 초기화 해야한다, 다만 블록이 실행될때 하나의 초기화 문장만 실행됨을 컴파일러가 확인 할 수 있다면
조건에 따른 값으로 초기화가 가능하다 - 객체의 내부값은 변경이 가능하다
- var
- 초기화 이후에도 값변경 가능
- 단 변수의 타입은 변경 불가하다
- 형변환이 필요
- 컴파일러는 변수 선언시점의 초기화식으로 타입을 추론하기 때문
- var 보다는 val 변수를 권장
- 프로그래밍에서 대부분의 경우 변수의 값을 변경할 필요가 없고, 변수를 불변으로 하는 경우 여러 면에서 유리하다는 것을 알게 되어서.
- 변수를 사용할때 최초 값 대입 이후로 굳이 값을 변경하지 않는 경우가 많다. 특히 임시적으로 사용되는 지역변수, 함수의 파라미터와 같은 경우 대부분 값을 변경하지 않는다
- 반대로 불변으로 선언할 경우, '메모리,멀티쓰레드 안전성, 함수형 코드'등 얻을수 있는 이점이 많다.
- 문자열 템플릿
// 문자열 템플릿
// 책 예제가 문제인지 args.size 1이 나온다 빈string이...
fun main(args: Array<String>) {
val name = if (args.size > 0) args[0] else "kotlin"
println(args[0] is String) //true?
println("hello, $name!")
}
// 복잡한식도 중괄호로 감싸서 사용가능
fun main(args: Array<String>) {
println("hello, ${if (args.size > 1) args[0] else "kotlin"}!")
}
//문자열 그대로 $name 을 출력할경우 이스케이프처리
fun main(args: Array<String>) {
val name = if (args.size > 1) args[0] else "kotlin"
println("hello, \$name!")
}
2.2 클래스와 프로퍼티
- 클래스
//java VO
public class Person {
private final string name;
public Person(String name) {
this.name = name
}
public string getName() {
return name;
}
}
//kotlin 변환
class Person (val name: String)
- 코틀린의 기본 가시성 public
- 프로퍼티
class Person(
// getter만 제공
val name: String,
// getter, setter 제공
var isMarried: Boolean
)
fun main() {
val person = Person("bob", true) // new 를 쓰지않고 생성가능
//get 메소드 대신 intance.property 로 접근가능
println(person.name)
println(person.isMarried)
//setter대신 직접 프로퍼티에 대입 (자꾸 파이썬의 향기가)
person.isMarried = false
println(person.isMarried)
}
>>>bob
>>>true
>>>false
- 자바에서 선언한 클래스를 코틀린 문법을 사용할 수 있다.
- getter를 val 프로퍼티로 사용
- getter, setter 다있는경우 var 프로퍼티로 사용
- 커스텀 접근자
class Rectagle(
val height: Int,
val width: Int
){
// isSquare get 할때 getter에서 계산된 값이 반환
// java 에서 호출시 isSquare()
val isSquare: Boolean
get() {
return height == width
}
val sumHw: Int
get() {
return height + width
}
}
fun main() {
val rectangle = Rectagle(40, 40)
println(rectangle.isSquare)
println(rectangle.sumHw)
}
>>>true
>>>80
- 커스텀 getter 를 사용하면 프로퍼티의 값을 그때 그때 계산하는 backing field 로 만들 수 있음
- 코틀린 소스구조 : 디렉터리와 패키지
package geometry.shapes // 패키지 선언
import java.util.Random // 자바 라이브러리 import
class Rectangle(
val height: Int,
val width: Int
){
val isSquare: Boolean
get() = height == width
}
fun createRectangle() : Rectangle {
val random = Random() //자바 랜덤 클래스 사용
return Rectangle(random.nextInt(), random.nextInt())
}
----------------
//위에 만든 패키지를 다른파일에서 import
import geometry.shapes.createRectangle
fun main() {
val rectangle = createRectangle()
println(rectangle.isSquare)
}
>>>false
- 코틀린 파일의 맨앞에 package 선언하면 파일안에 모든 클래스, 함수등이 패키지로 들어감
- import package.* 패키지 하위 모두 import
- 자바 패키지 처럼 계층구조를 강제하진 않지만 자바방식을 따르는게 좋다.
- 마이그레이션등에 문제가 생길수 있음
2.3 선택 표현과 처리 : enum 과 when
- enum 클래스의 정의
// enum 의 기본 선언
enum class Color {
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}
// 프로퍼티가 있는 enum
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
}
fun main() {
println(Color.BLUE.rgb())
}
>>>255
- when으로 enum 클래스 다루기
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), BLACK(0, 0, 0);
fun rgb() = (r * 256 + g) * 256 + b
}
fun getMnemonic(color: Color) =
when (color) {
Color.RED -> "Richard"
Color.ORANGE -> "Of"
Color.YELLOW -> "York"
Color.GREEN -> "Grave"
Color.BLUE -> "Battle"
Color.INDIGO -> "In"
Color.VIOLET, Color.BLACK -> "Vain"
}
fun main() {
println(getMnemonic(Color.VIOLET))
}
>>>Vain
- when : java의 switch를 대체
- break 넣지 않아도 됨
- 복수 매칭은 콤마구분
package ch02.colors
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), BLACK(0, 0, 0);
fun rgb() = (r * 256 + g) * 256 + b
}
----------
import ch02.colors.color
fun getMnemonic(color: Color) =
when (color) {
Color.RED -> "Richard"
Color.ORANGE -> "Of"
Color.YELLOW -> "York"
Color.GREEN -> "Grave"
Color.BLUE -> "Battle"
Color.INDIGO -> "In"
Color.VIOLET, Color.BLACK -> "Vain"
}
fun main() {
println(getMnemonic(Color.VIOLET))
}
>>>Vain
- 상수값 import 사용 가능
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
}
fun mix(c1: Color, c2: Color) =
when (setOf(c1, c2)) {
setOf(Color.RED, Color.YELLOW) -> Color.ORANGE
setOf(Color.YELLOW, Color.BLUE) -> Color.GREEN
setOf(Color.BLUE, Color.VIOLET) -> Color.INDIGO
else -> throw Exception("dirty") //예외 값
}
fun main() {
println(mix(Color.YELLOW, Color.RED)) //set 객체에 넣으므로 순서는 중요치 않음
}
>>>ORANGE
---------------------
// when 인자없이 사용
fun mix(c1: Color, c2: Color) =
when {
(c1 == Color.RED && c2 == Color.YELLOW) ||
(c2 == Color.RED && c1 == Color.YELLOW) -> Color.ORANGE
(c1 == Color.YELLOW && c2 == Color.BLUE) ||
(c2 == Color.YELLOW && c1 == Color.BLUE) -> Color.GREEN
(c1 == Color.BLUE && c2 == Color.VIOLET) ||
(c2 == Color.BLUE && c1 == Color.VIOLET) -> Color.INDIGO
else -> throw Exception("dirty")
}
fun main() {
println(mix(Color.VIOLET))
}
- 분기 조건어 여러 객체를 조합해서 사용가능 (예제 참고)
- setOf : set 객체 생성
- 스마트 캐스트 타입 : 타입검사와 타입 캐스트를 조합
interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr
fun eval (e: Expr): Int =
if (e is Num){
val n = e as Num // 중복 캐스팅
n.value
} else if (e is Sum) {
eval(e.right) + eval(e.left) //스마트 캐스트
} else {
throw IllegalArgumentException("unknown expression")
}
fun main() {
println(eval(Sum(Num(1), Num(2))))
}
- 코틀린에서는 is type 을 이용해 타입검사를 한다
- is 로 검사하고 나면 자동 캐스팅 되는데 이를 스마트 캐스트라고 한다
- 스마트캐스트는 타입 검사이후 값이 바뀔 수 없는 경우 작동
- val 변수
- val 프로퍼티
- 타입 캐스팅 하려면 as 키워드 사용
- 리팩토링 : if를 when으로 변경
interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr
fun eval (e: Expr): Int =
when (e) {
is Num -> {
// 분기 코드 블록
e.value //스마트 캐스트 적용
}
is Sum -> {
eval(e.right) + eval(e.left)
}
else -> {
throw IllegalArgumentException("unknown expression")
}
}
fun main() {
println(eval(Sum(Num(1), Num(2))))
}
- fun eval if 식을 when 으로 변경
- if, when 모두 분기에 블록을 사용할 수 있다
2.4 대상을 이터레이션 : while과 for루프
- while 루프
- 자바와 동일 문법이다
- 수에 대한 이터레이션 : 범위와 수열 (for)
// 지정 범위만큼 이터레이션
fun fizzBuzz(i: Int) = when {
i % 15 == 0 -> "FizzBuzz"
i % 3 == 0 -> "Fizz"
i % 5 == 0 -> "Buzz"
else -> "$i"
}
fun main() {
for (i in 1..15) { // .. 연산자 시작과 끝값을 연결해서 범위를 만든다
println(fizzBuzz(i))
}
}
>>>1
2
Fizz
4
......
---------------------------
// 증가 값을 가지고 이터레이션
fun fizzBuzz(i: Int) = when {
i % 15 == 0 -> "FizzBuzz"
i % 3 == 0 -> "Fizz"
i % 5 == 0 -> "Buzz"
else -> "$i"
}
fun main() {
for (i in 20 downTo 1 step 2) {//20 부터 2씩 감소
println(fizzBuzz(i))
}
}
>>>Buzz
Fizz
16
14
Fizz
Buzz
- downTo 역방향 수열
- 1..20 step 2 : 2씩 증가
- 끝값이 특정 값이 아닐때 (특정 크기 만큼)
- utill 함수 사용 (x in 0 util size)
- 맵에 대한 이터레이션 (foreach)
import java.util.*
fun main() {
val binaryReps = TreeMap<Char, String>()
for (c in 'a'..'f') {
val binary = Integer.toBinaryString(c.code)
binaryReps[c] = binary
}
for ((letter, binary) in binaryReps) { // foreach!
println(letter)
println(binary)
}
}
>>a
1100001
b
1100010
c
1100011
d
1100100
e
1100101
f
1100110
- in으로 컬렉션이나, 범위에 원소 검사
// 값이 범위에 속하는지 검사
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
fun isNotDigit(c: Char) = c in '0'..'9'
fun main() {
println(isLetter('q'))
println(isNotDigit('x'))
}
>>true
false
-------------------------
// when에서 조건으로 사용
fun recognize(c: Char) = when (c) {
in '0'..'9' -> "digit"
in 'a'..'z', in 'A'..'Z' -> "letter"
else -> "i dont know"
}
fun main() {
println(recognize('q'))
}
>>letter
- 비교가 가능한 클래스라면(comparable인터페이스 구현)범위를 만들고 비교가능
- 모든 객체를 이터레이션 할순 없지만 in을 사용하여 속해있는지 비교는 가능하다
2.5 코틀린의 예외처리
import java.io.BufferedReader
import java.io.StringReader
// 자바와 크게 다른 부분 없는 예외
fun readNumber(reader: BufferedReader): Int? { // ? 함수가 null 반환가능함 명시
try {
val line = reader.readLine()
return Integer.parseInt(line)
} catch (e: NumberFormatException) {
return null
}finally {
reader.close()
}
}
fun main() {
val reader = BufferedReader(StringReader("239"))
println(readNumber(reader))
}
--------------
// 식으로 예외 사용
import java.io.BufferedReader
import java.io.StringReader
fun readNumber(reader: BufferedReader) {
val number = try {
Integer.parseInt(reader.readLine())
} catch (e: NumberFormatException) {
null
}
println(number)
}
fun main() {
val reader = BufferedReader(StringReader("not a number"))
readNumber(reader)
}
>>>null
- 식으로 예외를 받아서 처리할수 있는 부분이 중요한 차이!
'발전로그 > 책장로그-개발' 카테고리의 다른 글
kotlin in action 03 (3.3) (0) | 2022.02.16 |
---|---|
kotlin in action 03 (3.1 ~ 3.2) (0) | 2022.02.15 |
클린 아키텍처 - 소프트웨어 구조와 설계의 원칙 02 3장 (1) | 2022.01.16 |
클린 아키텍처 - 소프트웨어 구조와 설계의 원칙 01 2장 (5) | 2022.01.09 |
클린 아키텍처 - 소프트웨어 구조와 설계의 원칙 01 1장 (0) | 2022.01.09 |