Kotlin

don9wan·2021년 9월 14일
0

Kotlin

목록 보기
1/3
post-thumbnail

왜, 코틀린을 사용하는가?

해당 질문에 답하는 것은 쉽지 않았다. 필자는 프로그래밍 언어를 Java로 시작했고 한 학기 내내 사용했다. Java 특유의 쓸데없이 긴 문장도 손에 완벽히 익었을 터, 때문에 JetBrain이 "A modern programming language that makes developers happier." 라는 슬로건을 내걸어도 별 생각이 없었다. 나름 편하게 사용하던 Java를 버리고 코틀린을 사용할 필요를 못 느꼈기 때문이다.

위의 생각은 취업준비를 하며 바뀌었다. 지금까지 필자가 하던 안드로이드 앱 프로젝트의 크기는 개인 단위이며 코드의 분량도 얼마 되지 않는다. Java를 쓰던 Kotlin을 쓰던 별 차이가 없었다. Kotlin을 어떻게 사용해야 하는지 몰랐던 이유도 있다. 하지만 프로젝트의 단위가 개인을 넘어, 협업이 필요한 수준의 단위로 프로젝트의 크기가 커지면 어떻게 될까?

보다 가독성이 좋은 언어를 통해, 효율성과 유지보수성을 높이는 것이 절실해질 것이다.
그래야 일은 적게하고 퇴근은 빨리 하니까

위의 조건을 만족하고, 기존 안드로이드 개발을 담당하던 언어인 Java와도 100% 호환성을 가진 프로그래밍 언어가 있다.

코틀린이다.
오늘은 구글을 헤집고 돌아다니며 찾아본 왜, 코틀린을 사용해야하는지에 대해 알아보겠다. 또한 코틀린의 대표적 기능 몇 가지를 소개하겠다. Java를 경험해본적이 없는 입문 개발자들도, 코틀린을 왜 사용하는지에 대해 알고 사용을 시작했으면 한다.

자바와 코틀린

-Type systemMulti-PlatformOOPFPType InferenceNullable
자바(Java)정적 타입OOXX(9 이하)X
코틀린(Kotlin)정적 타입OOOOO

같은 점

  • Type System
    • 정적 타입으로, 변수의 타입을 명시해줘야 한다.
    • 타입 에러로 인한 문제점을 초기에 발견할 수 있어 타입 안정성이 높다.
    • 물론 코틀린의 경우 타입추론이 가능하다.
  • 멀티 플랫폼
    • JVM(Java Virtual Machine)만 설치되어 있으면 어떤 운영체제에서도 애플리케이션이 작동한다.
  • OOP (Object Oriented Programming)
    • 일명 객체 지향 프로그래밍
    • 객체(Object) 단위로 나누어 진 객체들끼리 상호작용하는 방식
    • 큰 문제(프로젝트)를 작은 단위로 나누어 해결할 때 용이하다
  • 자동 메모리 관리(GC)
    • 포인터 개념이 없고 자동으로 메모리를 관리해준다.
    • JVM의 Garbage Collector이 해당 일을 수행한다.
  • 동적 로딩
    • 프로그램 실행 시 필요할 때마다 동적으로 메모리를 생성(Heap)하고
      필요없는 메모리는 자동으로 메모리에서 소멸시킨다.
    • Java에서 static 키워드가 필요한 이유이다.
    • staitc 키워드가 없는 메소드를 사용하기 위해서는
      반드시 클래스를 인스턴스화해야 메모리에 실행 가능한 상태가 된다.

다른 점(코틀린)

  • FP (Fuctional Programming)
    • 함수형 프로그래밍이 지원된다.
    • 코틀린에서 함수는 일급 객체이다.
    • 따라서 함수의 인자 및 반환값이 함수가 될 수 있다.
    • Java에서 class는 필수 사항이다.
    • Kotlin에서 class는 선택 사항이다.
  • Type Interface
    • 할당되는 literal 값을 알아서 추론해준다. (Java 9 이하 불가능)
  • 모든 타입이 클래스 타입
    • Nullable 타입을 위해 primitive type마저 wrapper 클래스로 존재한다.
      • int -> Integer
      • double -> Double
  • NPE 처리
    • 중요한 차이점은 NPE(Null Pointer Exception)에 대한 처리이다.
    • NPE : 객체에 할당된 값이 null인 경우, 해당 값을 호출했을 때 발생하는 에러
    • 코틀린은 NPE가 일어날 수 있는 상황에서는 컴파일이 안되도록 설계되어 있다.
    • Null을 확인하는 연산자, Nullable 타입이 존재한다.
  • 향상된 개발 편의성
    • Java에 비해 간결한 코드(문법)을 가지고 있다.
    • 따라서 코틀린은 높은 개발 편의성을 제공한다.

코틀린의 기능

1. Kotlin NPE 관련 연산자

  • Nullable?
    • 해당 타입(Nullable)에 ?을 붙임으로서 null이 가능한 변수임을 명시적한다.
  • nullCheck?.A
    • 해당 변수(nullCheck)가 null이 아니라면 A에 대해 실행하고
    • null이라면 null을 반환한다.
  • elvisOperator?:B
    • 해당 변수(elvisOperator)가 null이 아니라면 B 값을 반환한다.
    • null이라면 디폴트값 B를 반환한다.
  • safeCast as? Person
    • 변수 safeCast를 Person타입으로 casting을 시도한다.
    • casting이 불가능하면 null을 반환한다.
  • nullPass!!
    • 해당 변수(nullPass)가 null이 아님을 개발자가 보장
    • 임시방편에 불과한 방법이다. 다른 방법으로 문제를 해결하는 것이 좋다.
  • 왜 이렇게까지
    • Java 프로그래밍을 해본 경우에 null에 대한 처리를 자주해봤을 것이다.
    • #좀 편하게 합시다

2. Scope Function

스코프함수 이해를 위한 고차함수, 람다 를 먼저 보고 오자.

정의

1. 스코프함수에 객체의 인스턴스를 전달하고
2. 해당 인스턴스의 속성이나 함수를 "Scope 내에서 깔끔하게 분리하여" 작업한다.

  • 결과
    코드 가독성 및 유지보수성을 향상시킨다.
  • 특징
    람다식을 사용한다.

apply

  • 인스턴스 생성 직후 변수에 담기 전 초기화를 수행할 때 유용

  • 일반 람다 함수와 다르게 해당 인스턴스를 변수에 반환해준다.

    • apply 적용 전

      var book1 = Book("5England 서사시", 10000)
      book1.name = "할인판 " + book1.name
      book1.discount()
      
    • apply 적용 후

      var book1 = Book("5England 서사시", 10000).apply{
        name = "할인판 " + name
        discount()
      }

run

  • 인스턴스가 이미 생성된 후 인스턴스의 함수나 속성을 사용해야 할 때 유용
  • 일반 람다 함수처럼 마지막 코드의 값을 변수에 반환해준다.
    • run 적용 전
      book1.name = "초특가 " + book1.name
      book1.discount()
      var newPrice = book1.price
    • run 적용 후
      var newPrice = book1.run(
         name = "초특가 " + name
         discount()
         price
      }
      

with

  • "run과 기능 동일"
  • 차이점
    • run : 인스턴스를 참조 연산자로 받는다.
    • a.run{ ~ }
    • with : 인스턴스를 파라미터로 받는다.
    •  with(a){ ~ }
  • 일반적으로 run이 왜 더 많이 쓰이는가?
    • run 사용 시 인스턴스에 (?.) 를 붙여 nullCheck 기능을 사용할 수 있기 때문
    • with(a?.){}
    • 이렇게 쓰면 큰 일 난다
    • nullCheck 기능은 위에서 설명했다.

apply-run-with vs also-let

  • 일단 타임

    • 소제목을 보면 also와 let이 남아 있다는 것을 알 수 있다.
    • 스크롤을 내려보면 알겠지만, also-let은 apply-run-with과 기능이 동일하다고 보면 된다.
  • 그렇다면, 대체 왜 이들을 구분해 놓았는지 명확히 알아야 스코프 활용성을 높일 수 있을 것이다.

  • 차이점

    • apply/run/with : 참조연산자(it) 없이 인스턴스의 변수와 함수를 사용할 수 있다.
    • also/let : 마치 파라미터를 넘긴 것 처럼, 참조연산자(it)를 통해 인스턴스의 변수와 함수를 사용할 수 있다.
  • 참조연산자 it

    • 스코프 함수에 전달된 해당 인스턴스를 it이라는 키워드로 참조할 수 있게 해준다.
  • 왜 also와 let은 참조연산자를 사용해야 하는가? :

    • 같은 이름의 변수 및 함수가 scope 바깥에 중복되어 있는 경우의 혼란을 방지한다.

    • fun main() {
         var price = 5000
         
         var book1 = Book("5England 서사시", 10000).apply{
            name = "할인판 " + name
            price = 8000
         }
         
         book1.run{
            println(name + ", " + price + "원")
      }
      <<출력>> 할인판 5England 서사시, 5000
    • run함수가 scope 내 변수 price보다 main 함
      수 내 변수 price를 우선시
      하고 있기 때문이다.

    • apply, with도 위의 문제를 발생시킬 수 있다.

    • 따라서 위의 상황 시, 밑의 그림처럼 run이 아닌 let을 사용하면 된다.

also

  • apply의 기능을 가짐 : 처리가 끝나면 인스턴스를 반환
  • 참조연산자(it)을 사용함

let

  • run의 기능을 가짐 : 처리가 끝나면 최종값을 반환
  • 참조연산자(it)을 사용함
    • 이미 생성된 인스턴스가 null이 아닐 경우에 이를 실행하는 목적으로, instance?.let{ — }처럼 사용을 많이 한다.
    • “통상적으로 let을 null체크와 함께 자주 쓰는 것이다.”



정리

-applyrunwithalsolet
사용 목적(인스턴스를)초기화멤버 사용멤버 사용초기화멤버 사용
리턴값인스턴스마지막 코드마지막 코드인스턴스마지막 코드
참조연산자this(생략 가능)this(생략 가능)this(생략 가능)itit

변수 선언

  • val

    • value
    • Can't change the value after initialization.
  • var

    • variable
    • Can change the value after initialization.
  • Type Interface

    • 타입추론
    • var 혹은 val로 지시한 변수에 값을 직접 입력하거나 문법적으로 타입을 예상할 수 있는 경우, 타입을 명시할 필요가 없으며 컴파일러가 자동으로 타입을 추론한다.
    • 코드량을 줄일 수 있지만 가급적 안 쓰는 것을 추천한다.
    • 반드시 특정 자료형으로 지정해야하는 상황이 아니라면 타입추론 기능을 이용하여 코드량을 줄일 수 있다.
    • int i = 5;   //java
       var i = 5    //kotlin Type Interface O
       var i : Int = 5    //kotlin Type Interface X
  • 상수

    • const 키워드 사용
    • Java에서는 final
  • Late initialization

    • 나중에 초기화할게
      • lateinit var lateNumber : Integer
    • null이 할당될 수 없는 타입에 한하여 사용 가능하다.
      • nullable type 사용 불가능(Integer?)
      • primitive type 사용 불가능(Int)
  • Lazy initialization

    • 나중에 이 변수가 사용되면, 그 때 이값으로 초기화 할게
      • val lazyNumber : Int by lazy{ 100 }

class + field, property

  • field : 필드(멤버 변수)

  • property : 필드(멤버 변수) + 접근자(getter & setter)

    • Java는 field를 기본으로 하고,
    • Kotlin은 property를 기본으로 한다.
    • 변수 선언 키워드와 property
      • var : 값을 변경할 수 있으므로 getter, setter 모두 자동 생성된다.
      • val : 값을 변경할 수 없으므로 getter가 자동 생성된다.
      • 둘 다 사용 x : getter, setter 모두 생성되지 않는다.
    • class Person(name: String)
      • 생성자 자동생성
    • class Person(val name: String)
      • 생성자, getter 자동생성
    • class Person(var name: String)
      • 생성자, getter & setter 자동생성
  • data class

  data class Person(var name : String, var age : Integer)
  • 데이터를 저장하는 목적의 클래스, 데이터 클래스가 있다.
  • 기존(Java)에서는 변수 생성, 생성자, getter & setter를 직접 기술해줘야 했지만
  • 위와 같이 data class를 사용하면 코드 한 줄만에 이 모든 것들이 자동 생성된다
  • 접근자 커스텀
class Person( _name : String ){
   var name : String = nameStr
       get() {
           return this.name
       }
       set(newName : String) {
           this.name = newName
       }
 }
  • 이 외에
    • inner class : 클래스 안에 클래스 정의 가능
    • enum class : 상수로 이루어진 클래스
profile
한 눈에 보기 : https://velog.io/@dongwan999/LIST

0개의 댓글