[Android] Kotlin 언어 알아보기

Kotlin Syntax

Posted by 동식이 블로그 on March 10, 2020

Kotlin 언어 알아보기

앱 개발 직군에 들어가면서 kotlin을 배워야 하는 상황이 되었다

1. 변수선언

  • Kotlin은 두 키워드(valvar)를 사용하여 변수를 선언한다

val

  • 값이 변경되지 않는 변수에 val을 사용한다
  • val을 사용하여 선언된 변수에 값을 다시 할당할 수 없다
1
2
// languageName의 값을 항상 유지하기 위해 val 사용
val languageName: String = "Kotlin"

var

  • 값이 변경될 수있는 변수에 var을 사용한다
1
2
3
4
// count는 초기 값 10이 할당되는 Int 유형의 변수
var count: Int = 10
	count = 15
// var 키워드를 사용했기 때문에 count 값을 재할당 할 수 있다

Int는 정수를 나타내며, 다른 언어와 마찬가지로 숫자 데이터에 따라 Byte, Short, Long, Float, Double을 사용할 수도 있다

유형추론

  • Kotlin은 정적으로 입력되는 언어이다. 즉, 컴파일 시간에 유형이 확인되고 절대 변경되지 않는다
1
2
3
4
5
6
7
val languageName = "Kotlin"
	val upperCaseName = languageName.toUpperCase()
	
	// 컴파일 실패
	/* 실패이유는 languageName이 String으로 추정되므로 String 
	클래스의 일부가 아닌 함수를 호출할 수 없다 */
	languageName.inc()
  • 위의 예에서 toUpperCase()는 String 유형의 변수에서만 호출 할 수 있는 함수인데, Kotlin 컴파일러가 languageName을 String으로 추정ㄹ했으므로 호출이 가능
  • 하지만 inc()는 Int 연산자 함수이므로 String에서 호출할 수 없다

Null 안전

  • Kotlin 변수는 기본적으로 null값을 보유할 수 없다
1
2
// 컴파일 실패
val languageName: String = null
  • null값을 포함하는 변수는 nullable 유형이어야 한다.
  • ?를 변수 접미사로 지정하여 변수를 nullable로 지정할 수 있다
1
val languageName: String? = null
  • nullable 변수는 신중하게 처리하지 않으면 NullPointerException이 발생한다

2. 조건부

다른 언어의 if-else와 유사하다

1
2
3
4
5
6
7
if (count == 42) {
    println("I have the answer.")
} else if (count > 35) {
	println("The answe ris close.")
} else {
    println("The answer eludes me.")
}
  • 조건문은 스테이트풀(stateful) 논리를 나타내는 데 유용하지만 작성시 반복될 수 있다. 이 반복을 피하기 위해 조건식을 제공한다
1
2
3
4
5
6
7
8
9
val answerString: String = if (count == 42){
    	"I have the answer."
	} else if (count > 35) {
    	"The answe ris close."
	} else {
    	"The answer eldues me."
	}

	println(answerString)
  • 암시적으로 각 조건부 분기는 마지막 줄에 표현식의 결과를 반환하기 때문에 return 키워드를 사용할 필요가 없다
  • 위의 세 분기의 결과는 모두 String 유형이므로 if-else 표현식의 결과도 String유형이다.
  • answerString에는 if-else 표현식의 결과에서 초기 값이 할당된다
  • 유형추론을 사용하여 answerString에 대한 명시적 유형 선언을 생략할 수 있지만, 명확히 하기 위해 유형 선언을 포함하는것이 좋다
1
2
3
4
5
6
val answerString = when {
    count == 42 -> "I have the answer."
    count > 35 -> "The answer is close."
    else -> "The answer eldues me."
	}
	println(answerString)
  • when 표현식의 각 분기는 조건, 화살표(->) 및 결과로 표시된다

  • 화살표의 왼쪽 조건이 true로 평가되면 오른쪽에 있는 표현식의 결과가 반환된다

  • when 표현식 코드는 if-else 코드와 기능적으로 동일하지만 쉽게 읽을 수 잇다

  • Kotlin의 조건부는 스마트 캐스팅(smart casting)을 강조한다

    • safe-call 연산자 또는 not-null assertion 연산자를 사용하여 nullable 값을 처리하는 대신 아래 예와 같이 조건식을 사용하여 변수에 null 값에 대한 참고아 있는지 확인할 수 있다
    1
    2
    3
    4
    5
    
    val language: String? = null
    	if (language != null) {
            // languageName?.toUpperCase()를 쓸 필요가 없다
            println(languageName.toUpperCase())
        }
    
    • 조건부 분기 내에서 languageName은 nullable이 아닌 것으로 간주될 수 있다.
    • kotlin에서는 분기 실행 조건에 따라 languageName은 null 값을 보유할 수 없으므로 분기 내에서 languageName을 nullable로 처리할 필요가 없다

3. 함수

  • 하나 이상의 표현식을 함수로 그룹화 할 수 있다
  • 함수를 선언하려면 fun 키워드 뒤에 함수 이름이 오도록 사용한다
  • 그런 다음 함수에 사용되는 입력 유형(있는 경우)을 정의하고 반환하는 출력 유형을 선언한다
1
2
3
4
5
6
7
8
9
10
fun generateAnswerString() : String {
    val answerString = if (count == 42) {
        "I have the answer"
    } else {
        "The answer eludes me"
    }
    return answerString
}
// answerString 변수는 generateAnswerString()의 결과에 따라 초기화된다
val answerString = generateAnswerString()
  • 위의 예에서 함수의 이름generateAnswerString이다
  • 입력 값은 받지 않으며, String 유형의 결과를 출력한다
1
2
3
4
5
6
7
8
9
10
fun generateAnswerString(countThreshold: Int): String{
    val answerString = if (count > countThreshold) {
        "I have the answer"
    } else {
        "The answer eludes me."
    }
    return answerString
}
// 인수를 포함한 함수 호출
val answerString = generateAnswerString(42)
  • 위의 예에서 gernerateAnswerString()Int 유형의 countThreshold인수 한개를 사용한다

함수 선언 단순화

1
2
3
4
5
6
7
fun generateAnswerString(countThreshold: Int): String{
    return if (count > countThreshold) {
        "I have the answer"
    } else {
        "The answer eludes me"
    }
}
  • 위의 예에서 단일 표현식의 결과가 함수에서 반환되는 경우 포함된 if-else 표현식의 결과를 직접 반환하여 로컬 변수 선언을 건너뛸 수 있다
1
2
3
4
5
fun generateAnswerString(countThrehold: Int): String = if (count > countThreshold) {
    "I have the answer"
} else {
    "The answer eludes me"
}
  • 위의 예처럼 return 키워드를 대입 연산자로 바꿀 수 있다

익명함수

  • 모든 함수에 이름이 필요하지 않으며, 입력과 출력에 의해 직접적으로 식별된다
  • 참조를 사용하여 나중에 익명 함수를 호출하면 익명 함수에 대한 참조를 유지할 수 있다
1
2
3
val stringLengthFunc: (String) -> Int = { input ->
        input.length
    }
  • stringLengthFuncString을 입력으로 사용하고, String 입력 길이를 Int 유형의 출력으로 반환하는 익명 함수에 대한 참조를 포함한다
  • 따라서 함수의 유형은 (String) -> Int로 표시된다
1
2
3
4
5
val stringLengthFunc: (String) -> Int = { input ->
        input.length
    }
	// 함수 호출
    val stringLength: Int = stringLengthFunc("Android")

고차 함수

  • 함수는 다른 함수를 인수르 취할 수 있다
  • 다른 함수를 인수로 사용하는 함수를 고차 함수라고 한다
1
2
3
fun stringMapper(str: String, mapper: (String) -> Int): Int {
    return mapper(str)
}
  • 예에서 stringMapper()함수는 전달된 String에서 Int값을 파생하는 함수와 함께 String을 가져온다

4. 클래스

  • 맞춤 유형을 추가하려는 경우 class 키워드를 사용해 클래스를 정의할 수 있다
1
class Car

속성

  • getter, setter 및 backing 필드를 포함할 수 있는 클래스 수준 변수이다
1
2
3
class Car {
    val wheels = listOf<Wheel>()
}
  • 위의 예제는 Wheel 객체 목록을 Car의 속성으로 추가한다
1
2
var car = Car()
val wheels = car.wheels
  • wheelspublic val이다. 즉, Car 클래스 외부에서 엑세스 할 수 있지만 다시 할당할 수 없다
  • Car의 인스턴스를 가져오려면 위의 예 처럼 먼저 생성자를 호출해야 한다
1
class Car(val wheels: List<Wheel>)
  • 위의 예에서 클래스 생성자는 List<Wheel>을 생성자 인수로 취하고 인수를 사용하여 wheels속성을 초기화 한다

클래스 함수 및 캡슐화

  • 클래스는 함수를 사용하여 동작을 모델링 하는데, 함수는 상태를 수정할 수 있으므로 노출하려는 데이터만 노출할 수 있다
  • 이러한 엑세스 제어를 캡슐화라고 한다

참고사이트