Kotlin In Action) 2장 코틀린 기초

주효은·2024년 4월 25일
2

함수

함수 - 1) 값을 반환하지 않는 함수

fun main(args: Array<String>){
		println("Hello, World!")
		}
  • 코틀린은 함수를 최상위 수준에 정의할 수 있다

      ⇒ 자바 처럼 class안에 함수를 넣어야 할 필요가 없다는 뜻
  • 파라미터의 이름 뒤에 파라미터의 타입을 쓴

함수 - 2) 값을 반환하는 함수

코틀린에서의 if는 문장이 아니라 결과를 만들어 내는 식이다.

  • 문 (statement)와 식(expression)의 차이 식 expression : 값을 만들어내며 다른 식의 하위 요소로 계산에 참여할 수 있다 문 statement : 자신을 둘러싸고 있는 가장 안쪽 블록의 최상위 요소로 존재함, 값을 만들지 X

함수 - 3) 식이 본문인 함수

  • 중괄호가 없고 return을 제거하면서 등호를 붙인 함수
fun max(a: Int, b: Int) : Int = if (a > b) a else b
  • 식이 본문이지 않은 함수는? 위의 2-1에서 보인 함수와 같이 ‘본문이 중괄호로 둘러싸인 함수’

코틀린에서는, 식이 본문인 함수가 많이 쓰인다.

또한, 앞서 말했던 코틀린의 ‘타입 추론’기능에 의해 반환 타입을 생략하여 아래와 같이 적을 수 있다

fun max(a: Int, b: Int) = if (a > b) a else b

그러나 이는, ‘식이 본문인 함수’의 반환 타입만 생략 가능하다.

블록이 본문인 함수가 값을 반환한다면, 반드시 반환 타입을 지정해야된다.

변수

변수 - 1) 변수 선언

타입을 지정을 생략하는 경우 (대부분의 경우)

val a = "나는 코틀린 스터디 중"
val b = 23

타입을 추가하고 싶다면?

val b: Int = 23

초기화 식을 사용하지 않고 변수를 선언하고 싶다면? (*반드시 타입 지정을 하자)

val c: Int
c = 23

변수 - 2) 변경 가능한 변수와 변경 불가능한 변수

val

변경 불가능한 참조를 저장하는 변수, 재대입이 불가능하다

var

변경 가능한 참조, 변수의 값은 바뀔 수 있다

** 기본적으로 val로 선언하고, 나중에 꼭 필요할 때만 var로 선언하는 것을 권장

  • 함수형 코드와 val의 관계? 변경 불가능한 참조와 변경 불가능한 객체를 부수 효과가 없는 함수와 조합해서 사용하면 코드가 함수형 코드에 가까워진다.

val의 특성

(1) 어떤 블록이 실행될 때, 오직 한 초기화 문장만 실행됨을 컴파일러가 확인할 수 있다면 조건에 따라 val값을 다른 여러 값으로 초기화 할 수 있다.

val message: String
if (canPerf○rmOperati○n()) {
message='Success"
//...연산을수행한다
l
else {
messaqe=''Failed"
)

(2) val 참조 자체는 불변일지라도 그 참조가 가리키는 객체의 내부 값은 변경될 수 있다.

val lang = arrayListOf("Java") //불변 참조를 선언
lang.add("Kotlin") //참조가 가리키는 객체 내부를 변경

var의 특성

(1)var을 이용하여 변수의 값을 변경할 수는 있지만, 타입은 유지된다.

var ans = 42
ans = "코틀린 공부 중" //변수 타입이 달라 컴파일 오류 발생

문자열 템플릿

  • 변수명 앞에 $을 붙인
fun main(args: Array<strlng>) {
		val name = if(args.slze>0) args[0] else''Kotlin'
    Println(''Hello,$name") 
}
  • $를 출력하고 싶다면?
    println("\$x")
    // \이스케이프기호를 이용해라

클래스

//자바의 경우

public Class Person {
 prlvate final String name;
 
 public Person(String name) {
		this.name=name;
	}
public Strinq getName () {
		return name
		}
	}

위와 같은 내용을 코틀린으로 변경하면

 class Person (val name: String) 

값 객체 : 위와 같이 코드 없이 데이터만 저장하는 클래스

** 코틀린의 기본 가시성은 public이므로 public 가시성 변경자는 생략됨

프로퍼티

프로퍼티 - 1 ) 프로퍼티 선언

클래스 안에서 변경 가능한 프로퍼티 선언하기

class Person(
		val name: String // 읽기 전용 프로퍼티
		var isMarried: Boolean // 쓰기 전용 프로퍼티
	)

val 프로퍼티 : 게터만 선언

var 프로퍼티 : 게터와 세터 모두 선언

코틀린에서 Person 클래스 사용하기

  • 게터와 세터의 이름을 정하는 규칙의 예외

게터
이름이 is로 시작하는 프로퍼티의 게터 ⇒ get이 붙지 않고 이름 그대로 사용 됨

(is로 시작하지 않는 그냥 프로퍼티를 사용해도 코틀린이 자동으로 케터를 호출해줌)

세터

이름이 is로 시작하는 프로퍼티의 세터 ⇒ is를 set으로 바꾸기

예시

```kotlin
class Love(
		var isLove: Boolean = true
		)

Love.setLove = false
```

new 키워드를 사용하지 않고, 생성자를 호출할 수 있다

val person = Person("hyoeun", false)
println(person.name) //hyoeun 출력
println(person.isMarried) // false출력

커스텀 접근

class Rectangle(val height: Int, val width: Int) {
0	val isSquare: Boolean
		get() { 
			return heigth == width
		}
}

이 프로퍼티에는 자체 구현을 제공하는 getter만 존재한다.

property에 접근할 때마다 getter가 property 값을 매번 다시 계산한다.

코틀린 소스트리 구조 : 디렉터리 패키지

자바의 경우

위와 같이 패키지의 구조와 일치하는 디렉터리 계층 구조를 만들고, 클래스의 소스코드를 그 클래스가 속한 패키지와 같은 디렉터리에 위치시켜야 한다

코틀린의 경우는 다음과 같다.

별도의 디렉터리를 만들지 않고, geometry라는 폴더 안에 shape.kt를 넣어도 되나, 자바와 같이 패키지별로 디렉터리를 구성하는 것을 지향한다.

  • 왜 그럴까?

    자바와 코틀린을 함께 사용하는 프로젝트에서는 자바의 방식을 따르지 않으면, 자바 클래스를 코틀린 클래스로 *마이그레이션할 때 문제가 생길 수도 있다.

  • 마이그레이션 이란?

    데이터나 시스템이 한 환경에서 다른 환경으로 옮겨지는 과정.

    파일을 A에서 B로 옮기는 단순한 과정이 아닌 데이터의 변환, 적용, 새로운 환경에서의 통합 과정도 모두 포함된 복잡한 과정이다.

선택 표현과 처리: enum과 when

enum - 1 ) enum 클래스 정의

enum 클래스는 다음과 같이 정의한다

enum class Color {
		RED, ORANGE, YELLOW, GREEN
		}

enum - 2 ) 프로퍼티와 메소드가 있는 enum 클래스 정의하기

enum class Color (
		val r: Int, val g: Int, val b: Int // 상수의 프로퍼티 '정의'
		)  {
					RED(255, 0, 0), ORANGE(255, 165, 0).. **;**
					// 각 상수를 생성할 때 그에 대한 프로퍼티 '값을 지정'
					***// enum 상수 목록과 메소드 정의 사이에 세미콜론 필수***
					fun rgb() =( r*256 + g )*256 + b
					// enum 클래스 안에서 메소드를 정의한다
				}
		

enum - 3 ) when으로 enum 클래스 다루기

  • 자바의 switch에 해당하는 코틀린 구성 요소는 when이다.
  • 자바와 달리 각 분기에 break를 넣지 않아도 된다.
fun getMnemonic( color: Color) = */**/ when도 값을 만들어 내는 식이다
																 // 식이ㅣ 본문인 함수에 바로 when을 쓸 수 있다***
		
		when (color) {
				Color.RED -> "R"
				Color.ORANGE -> "O"
				.
				.
				.
			}

+) 한 when 분기 안에 여러 값을 사용할 때는, 콤마로 구분해 주면 된다.

fun getMnemonic( color: Color) =
		******when (color) {
				Color.RED, Color.BLUE, Color.YELLOW -> "R"
				Color.ORANGE -> "O"
				.
				.
			}

+) when 분기 조건에 여러 다른 객체 사용하기

fun mix(c1: Color, c2: Color) =
		when **(setOf(c1, c2)**) {
		***// when 식의 인자로 아무 객체나 사용할 수 있다***
				setOf(RED, YELLOW) -> ORANGE
				.
				.
				else -> throw Exception("Dirty Color")
				}
  • setOf 여러 객체를 포함하는 집합을 Set객체로 만드는 함수

스마트 캐스트

  • 코틀린에서는 is를 사용해 변수 타입을 검사한다 (자바의 instanceof와 비슷)
  • 프로그래머 대신 컴파일러가 캐스팅을 해준다.
if (e is Sum) {
	return eval(e.right) + eval(e.left)
	}

is를 이용하여 Sum 타입인지 검사한 다음에는 e.right와 e.left를 사용할 수 있다.

  • as를 이용하면 원하는 타입으로 타입 캐스팅 할 수 있다
val n =  e as Num

if문으로 만든 eval 함수

fun eval(e: Expr) : Int =
		if (e is Num) {
			e.value
		} else if (e is Sum) {
			eval(e.right) + eval(e.left)
		}
		else {
			throw IllegalArgumentException("Unknown expression")
		}
  • if 분기에 식이 하나밖에 없으면 중괄호를 생략해도 된다

⇒ if 중첩 대신 when을 사용한다면?

fun eval(e: Expr) : Int =
		when(e) {
				 is Num -> **// 인자 타입을 검사하는 when 분기**
						 e.value
				
				 is Sum -> 
						 eval(e.right) + eval(e.left)
					
				else ->
						 throw IllegalArgumentException("Unknown expression")
				}

대상을 이터레이션: while과 for 루프

while 루프

[ while문 ]

while (조건) {
	/*내용*/
	}

조건이 참인 동안 본문을 반복 실행한다

[ do-while문 ]

do {
	/*내용*/
} while (조건)

맨 처음 본문을 한 번 실행한 다음

조건이 참인 동안 본문을 반복 실행

범위와 수열

.. < 연산자로 시작 값과 끝 값을 연결해서 범위를 만든다.

val oneToTen = 1..10 (1~10의 범위에서)

이터레이션 - 1 ) 범위 이터레이션

for (i in 100 downTo 1 step 2) 
// downTo : 역방향 수열
// step 2 : 증가 값 2

 출력 >> 100 98 96 .. 

in

in 연산자를 사용해 어떤 값이 범위에 속하는지 검사할 수 있다.


fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
println(isLetter('q')) // true 출력

+) when에서 in 연산자 사용하기

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.."
}

in 연산자 범위는 문자에만 국한되지 않는다, 비교가 가능한 클래스라면 그 클래스의 인스턴스 객체를 사용해 범위를 만들 수 있다

코틀린의 예외 처리

함수는 정상적으로 종료할 수 있지만 오류가 발생하면 예외를 던질 수 있다(throw).

코틀린의 throw는 식이므로 다른 식에 포함될 수 있다

val percentage = 
		if(number in 0..100)
			number
		else
			throw IllegalArgumentException(
				"A percentage value must be between 0 and 100 : $number")

try, catch, finally

예외를 처리하기 위해 try, catch, finally 절을 함께 사용한다

try를 식으로 사용하기

fun readNumber(read: BufferedReader) {
			val number =try {
			  Integer.parseInt(reader.readLine())
			} catch (e: NumberFormatException) {
				return
			}
			println(number)
		}

코틀린의 try 키워드는 if나 when과 마찬가지로 식으로 쓰인다.

catch에서 값 반환하기

fun readNumber(reader: BufferedReader) {
	val number = try {
		Integer.parseInt(reader.readLine())
		} catch(e: NumberFormatException) {
			null
		}
		println(number)
		}

0개의 댓글