KotlinLanguage

Kotlin 무작정 따라해보기 3일차 - Variables, Null Safety, Classes

3일차 시작하기 - Variables, Null Safety, Classes

지난 2일차에 이어서 Learn Kotlin by Example > IntroductionVariables, Null Safety, Classes에 대한 학습을 시작 합니다.

Variables

코틀린에서는 강력한 타입 추론을 지원하기 때문에 명시적인 타입을 지정 가능하지만, 타입을 지정하지 않아도 사용이 가능하다.(Typescript 처럼 타입을 지정해도 되고, 안해도 되고)
일반적으로 컴파일러가 타입을 유추해서 사용하도록 하는 편이다.(권장이라기 보다는 타입을 지정하지 않아도 상관 없으니 매번 입력할 필요가 없다라는 뜻인듯.)
변수는 mutable변수(값을 변경 할 수 있는, Javascript let), immutable변수(값을 변경 할 수 없는, Javascript const)가 있는데 코틀린에서는 immutable변수의 사용을 권장 한다.

var a: String = "initial"  // 1
println(a)
val b: Int = 1             // 2
val c = 3                  // 3
  1. var 키워드를 이용해서 String타입의 이름이 a인 변수를 선언하고 값을 initial으로 초기화 했다. 이는 mutable변수로 값 변경이 가능하다.

    • var 키워드가 Javascriptlet과 같은 역활 인듯 하다.
    • 표현방식은 키워드만 다를뿐, Typescript와 같은 것 같다.
  2. val 키워드를 이용해서 Int타입의 이름이 b인 변수를 선언하고 값을 1로 초기화 했다. 이는 immutable변수로 값 변경이 불가능 하다.

    • val 키워드가 Javascriptconst와 같은 역활 인듯 하다.
    • val로 선언된 변수는 immutable한 변수이기 때문에 새로운 값을 대입하지 못한다.
  3. val 키워드를 이용해서 타입의 지정없이 이름이 c인 변수를 선언하고 값을 3으로 초기화 했다.

    • Typescript처럼 타입을 명시적으로 지정하지 않아도 초기화시 들어가는 데이터에 따라 컴파일시에 자동으로 타입을 지정 한다.
var e: Int  // 1
println(e)  // 2
  1. var 키워드를 이용해서 Int타입의 이름이 e인 변수를 선언 하였다.
  2. 변수 e를 출력 한다.

    • 실행하면 Variable 'e' must be initialized에러가 발생 한다.
    • var로 변수를 선언 할 때 변수를 초기화할 값을 지정하지 않으면 에러가 발생 한다.
    • 변수를 선언하고 초기화는 언제든 해도 상관 없지만, 사용하기전에는 초기화를 해야 한다.

fun someCondition() = true

fun main() {
    val d: Int  // 1

    if (someCondition()) {
        d = 1   // 2
    } else {
        d = 2   // 2
    }

    println(d) // 3
}
  1. val키워드를 이용해서 Int타입의 이름이 d인 변수를 선언 하였다.
  2. someCondition()의 결과에 따라 d변수의 값을 1 또는 2로 지정 한다.

    • val의 경우 Javascriptconst 같은 불변의 변수(immutable)인데 바로 초기화 하지 않고 나중에 초기화를 하였다.
    • 실제 d변수를 사용하기 이전에 선언한 변수의 값을 초기화하면 문제가 없는 것 같다.
  3. 변수 d를 출력 한다.

Null Safety

NullPointerException을 발생시키지 않기 위해서, 코틀린은 기본적으로 변수에 Null을 할당을 허용하지 않는다. 하지만 Null이 필요한 경우를 위해서 변수 선언시 타입뒤에 ?를 붙여서 Null을 허용(nullable) 할 수 있다.

var neverNull: String = "This can't be null"            // 1

neverNull = null                                        // 2

var nullable: String? = "You can keep a null here"      // 3

nullable = null                                         // 4

var inferredNonNull = "The compiler assumes non-null"   // 5

inferredNonNull = null                                  // 6

fun strLength(notNull: String): Int {                   // 7
    return notNull.length
}

strLength(neverNull)                                    // 8
strLength(nullable)                                     // 9
  1. non-nullable String타입의 neverNull변수를 선언 하였다.

    • var키워드를 사용하여 mutable변수이기 때문에 값을 변경 할 수 있다.
  2. 1번에서 선언한 neverNull변수에 null을 재 할당 한다.

    • 코틀린은 기본적으로 Null을 허용하지 않기 때문에 Null can not be a value of a non-null type String에러가 발생 한다.
  3. nullable String타입의 nullable변수를 선언하였다.

    • var키워드를 사용하여 mutable변수이기 때문에 값을 변경 할 수 있다.
  4. nullable변수에 null을 재 할당 한다.

    • 타입 지정시 nullable로 선언하였기 때문에 문제 없이 null을 할당 할 수 있다.
  5. 타입을 지정하지 않고 inferredNonNull변수를 선언 하였다.

    • 코틀린은 타입을 지정하지 않고 컴파일시 추론할 때 non-nullable로 판단한다.
    • 만약 null을 허용하는 nullable로 사용하려면 명시적으로 nullable타입으로 지정해야 한다.
  6. 5번에서 선언한 inferredNonNull변수에 null을 재 할당 한다.

    • null을 할당 할 수 없는 non-nullable타입이기 때문에 Null can not be a value of a non-null type String에러가 발생 한다.
  7. non-nullable String을 파라미터로 받는 strLength()함수를 선언 한다.

  8. 7번에서 선언한 strLength()함수에 non-nullable로 선언한 neverNull변수를 인자로 넣어서 호출 한다.

    • strLength()함수 선언시 선언한 파라미터의 타입과 일치한 타입입의 인자를 입력하였기 때문에 문제 없이 정상적으로 함수가 호출 된다.
  9. 7번에서 선언한 strLength()함수에 nullable로 선언한 nullable변수를 인자로 넣어서 호출 한다.

    • strLength()함수 선언시 선언한 파라미터의 타입과 일치하지 않는 타입의 인자를 입력하여 Type mismatch: inferred type is Nothing? but String was expected에러가 발생 한다.
    • 함수(function)사용시 파라미터의 지정된 타입도 중요하지만, nullable여부도 중요하게 판단해야 한다.

Working with Nulls

코틀린은 기본적으로 null을 허용하지 않지만 외부의 자바코드와 상호작용하거나, 완전히 없는(선언되지 않은 것을 말 하는 것 같음)상태와 같이 null을 이용해야 하는 경우 아래 코드와 같이 처리 하면 된다.

fun describeString(maybeString: String?): String {              // 1
    if (maybeString != null && maybeString.length > 0) {        // 2
        return "String of length ${maybeString.length}"
    } else {
        return "Empty or null string"                           // 3
    }
}
  1. nullable String타입의 파라미터를 가지는 describeString()함수를 선언 하였다.
  2. describeString()함수 호출시 입력한 인자 maybeStringnull이 아니고, maybeString의 길이가 0보다 큰 경우 리턴되는 메시지.

    • maybeString != null 조건에 대한 비교가 먼저 되기 때문에 maybeStringnull이 아닌 경우에만 maybeString.length > 0 비교를 수행 한다.
    • &&AND 연산자로 앞/뒤 조건이 모두 일치 할 경우만 true 이다.

    String타입의 length를 출력 할 때 한글도 1글자로 인식한다. 유니코드를 사용해서 그런 것 같다. 함수 호출시 String문자열을 임의로 넣기 위해서 describeString('abcd')을 사용 하였는데, Too many characters in a character literal ''abcd'', The character literal does not conform to the expected type String?이런 에러메시지가 발생 함. 'abcd' => "abcd"로 변경하니 잘됨
    차이는 "" => String 타입 '' => Char 타입으로 추론 함.
    참고로, String(문자열)은 "로 감싸는 방식 말고 """("3개 사용)로 감싸는 방식도 존재
    참고: Kotlin Reference Basic Type: String
    참고: Kotlin Reference Basic Type: Char

    아래 코드는 String타입에서 값을 정의 할때 사용 가능한 표현에 대한 테스트(https://kotlinlang.org/docs/reference/basic-types.html#string-literals)

    fun main() {
        val string1: String = "큰따음표만 사용할 경우\n\\n을 넣어서 한줄을 띄울 수 있고"
        val string2: String = """큰따음표 3개를 사용 했을 경우
        이렇게 줄 변경해서 여러줄을 한번에 넣을 수 있음, 다만 \n와 같은 이스케이프 문자(?) 안먹힘
        """
        val string3: String = """큰따음표 3개의 경우 코드상의 줄마춤 대문에 의도치 않은
        |들여쓰기가 되는데 이것을 제거하는 방법으로
        |trimMargin()을 사용할 수 있음.
        |줄바뀜뒤 시작하는 문자의 제일앞에 prefix로 |를 기본 사용함
        """.trimMargin()
        val string4: String = """preifx로 사용하는 문자도 변경 가능함
        >trimMargin(">")의 인자로 원하는 문자를 입력하면됨
        """.trimMargin(">")
        println(string1)
        println(string2)
        println(string3)
        println(string4)
    }
    
    큰따음표만 사용할 경우
    \n을 넣어서 한줄을 띄울 수 있고
    큰따음표 3개를 사용 했을 경우
       이렇게 줄 변경해서 여러줄을 한번에 넣을 수 있음, 다만 \n와 같은 이스케이프 문자(?) 안먹힘
    
    큰따음표 3개의 경우 코드상의 줄마춤 대문에 의도치 않은
    들여쓰기가 되는데 이것을 제거하는 방법으로
    trimMargin()을 사용할 수 있음.
    줄바뀜뒤 시작하는 문자의 제일앞에 prefix로 |를 기본 사용함
    preifx로 사용하는 문자도 변경 가능함
    trimMargin(">")의 인자로 원하는 문자를 입력하면됨
    
  3. 2번의 조건이 false일 경우에 리턴 되는 메시지.

Classes

코틀린에서의 클래스 선언에 대한 간단한 예제를 통해 알아보는 것 같다. 클래스 부분은 예제의 내용보다 상당히 많은 내용이 있지만 이후 별도로 클래스만 다뤄서 다시 내용이 나오고, 여기서는 기초적인 선언하는 방법에 대해서만 알아보고자 한다.
Class는 이름, 헤더(클래스의 파라미터와 생성자(constructor) 등), 중괄호({})로 싸여진 본문으로 구성되고, 헤더와, 본문의 내용이 없을 경우 중괄호({})는 생략이 가능 하다.

class Customer                                  // 1

class Contact(val id: Int, var email: String)   // 2

fun main() {

    val customer = Customer()                   // 3

    val contact = Contact(1, "mary@gmail.com")  // 4

    println(contact.id)                         // 5
    contact.email = "jane@gmail.com"            // 6
}
  1. Customer 클래스를 선언하였다.

    • 헤더와 본문을 생략하고 이름만 선언 하였다.
    • Customer 클래스는 아무런 속성(properties)를 가지고 있지 않고, 생성자(constructor)의 경우 코틀린에서 기본 생성자로 자동 생성한다.
  2. immutable Int타입의 ID 파라미터와 mutable String타입의 email 파라미터를 가지는 Contact이름의 클래스를 선언 하였다.

    • 함수(function)를 선언하듯 ClassName(Parameter)형식으로 선언하면 지정한 Parameter가 해당 클래스의 속성(property)이 된다.
  3. customer변수에 Customer클래스의 기본 생성자를 이용해서 Customer Instance를 만들어 할당 한다.

    • 코틀린에서는 new키워드를 사용하지 않고 인스턴스(Instance)를 생성 한다.
  4. contact변수에 Contact클래스의 2개의 인자를 넘겨 Contact Instance를 만들고 할당 한다.

    • 코틀린의 기본 생성자를 이용하여 2개의 인자를 각각의 속성(property)에 할당하여 인스턴스를 생성한다.
  5. contactid 속성(property)의 내용을 출력 한다.
  6. contactemail속성(property)의 내용을 jane@gmail.com로 변경 한다.

Class에 대한 좀더 자세한 내용은 Kotlin Reference > Classes를 참고하자.

3일차 마무리 및 Next

3일차에는 코틀린의 변수와 클래스에 대해서 간단히 알아본것 같다.
변수(Variables)의 특징으로 기본적으로 Null을 허용하지 않고, Null의 사용하기 위해서는 타입을 지정하고 명시적으로 nullable로 선언해야 한다.
그리고 예제를 진행하면서 문자열을 사용하기 위한 String타입와 문자를 지정한 Char타입이 나뉘어져 있고, 타입을 지정하지 않을 경우 컴파일러가 타입 추론을 통해 타입을 유추하는데 이때 큰따음표(")와 작은따음표(')로 String, Char를 구분하기 때문에 JavascriptPHP에서 처럼 섞어서 사용할 수 없다.
클래스(Classes)의 경우는 매우 간단한하게 선언하고, 사용하는 방식에 대해서만 나왔고, 자세한 내용은 별도의 예제를 통해서 알려 줄것 같다.
다른 언어와 조금 다른점은 클래스를 사용할 때 new 키워드를 사용하지 않고, 그냥 함수를 호출하듯이 사용하면 된다는 것이다.
다음 시간에는 Generics과 상속(Inheritance)에 대해서 알아 보겠다.