당신은 주제를 찾고 있습니까 “kotlin in action 한글 pdf – How to make Pdf Reader App | Show pdf file from storage | Android Studio | Kotlin“? 다음 카테고리의 웹사이트 you.tfvp.org 에서 귀하의 모든 질문에 답변해 드립니다: you.tfvp.org/blog. 바로 아래에서 답을 찾을 수 있습니다. 작성자 Papermaxx 이(가) 작성한 기사에는 조회수 4,823회 및 좋아요 56개 개의 좋아요가 있습니다.
kotlin in action 한글 pdf 주제에 대한 동영상 보기
여기에서 이 주제에 대한 비디오를 시청하십시오. 주의 깊게 살펴보고 읽고 있는 내용에 대한 피드백을 제공하세요!
d여기에서 How to make Pdf Reader App | Show pdf file from storage | Android Studio | Kotlin – kotlin in action 한글 pdf 주제에 대한 세부정보를 참조하세요
#PDF #Reader #Android #kotlin How to make Pdf Reader App | Show pdf file from storage | Android Studio | Kotlin | 2020
Hello everyone! In this video, I shall show you how to make pdf reader app or pdf viewer app in android studio with kotlin programming language.
Requirement:
pdfviewer dependency Link: https://github.com/barteksc/AndroidPdfViewer
Step to Follow:
1. Create Project
2. add dependency in app level gradle
3. create button view on main activity
4. create intent to go another activity
5. add second activity with layout file to show pdf file
6. on pdf view activity
a. create function select pdf from storage
b. create function show pdf from uri
c. override on activity result method
Features:
1. Runtime Permission of External Storage.
2. Read all PDF files from Phone.
3. Display PDF files in Custom List View including icon and filename.
4. View PDF Files.
* How To Publish Android App On Play Store Free
https://www.youtube.com/watch?v=E0VHlLnTndM\u0026t=11s
* How to add Admob Banner Ads and Earn Money 100%
https://www.youtube.com/watch?v=53qsQuyDyWA\u0026t=103s
* How to Add Admob Interstitial Ad
https://www.youtube.com/watch?v=eyOJbf1Mxx4
android development tutorial,android app tutoria,android studio tutorial,learn android programming,android developer tutorial,android programming,android development,android studio tutorial for beginners,android course,android training,android development course,android app development course,android programming tutorial,android app development tutorial,android tutorial for beginners
kotlin in action 한글 pdf 주제에 대한 자세한 내용은 여기를 참조하세요.
Kotlin in Action(모바일 프로그래밍)(원서/번역서 – 교보문고
Kotlin in Action 코틀린 컴파일러 개발자가 직접 알려주는 코틀린 언어 핵심 | Kotlin 1.6 대응 / 자바스크립트 개발 관련 부록 추가. 모바일 프로그래밍.
Source: www.kyobobook.co.kr
Date Published: 8/11/2022
View: 6597
코틀린 인 액션 스터디 정리 자료 – 자바캔(Java Can Do IT)
예전에 신림프로그래머 페이스북 그룹에서 진행한 “코틀린 인 액션” 책 스터디 정리한 자료.
Source: javacan.tistory.com
Date Published: 6/4/2022
View: 7494
Kotlin in Action #1. 코틀린 시작하기 – lelecoder
‘Kotlin in Action’ 책을 학습하고 정리한 내용입니다. 1. 코틀린 시작하기. 코틀린은 자바 플랫폼에서 돌아가는 새로운 프로그래밍 언어이다. 자바 코드 …
Source: lelecoder.com
Date Published: 7/23/2021
View: 6601
Kotlin in Action – 프로그래밍 언어 – 알라딘
Kotlin in Action 코틀린 컴파일러 개발자가 직접 알려주는 코틀린 언어 핵심 (Kotlin 1.6 대응 / 자바스크립트 개발 관련 부록 추가).
Source: www.aladin.co.kr
Date Published: 2/18/2022
View: 4307
코틀린 언어 문서
Kotlin for Andro Developers 은 또 다른 좋은 자료이다. 이 책은 코틀린을 이용한 실제 안드로이드 어플리케이션 제작 과정을 단계적으로 안내한다.
Source: t1.daumcdn.net
Date Published: 11/15/2021
View: 4286
Kotlin in Action MEAP V11
It’s a new programming language targeting the Java platform. Kotlin is concise, safe, pragmatic, and focused on interoperability with Java code. It can be used …
Source: sd.blackball.lv
Date Published: 4/2/2022
View: 6832
Kotlin In Action 책을 읽자 – 준호씨
왜인지 며칠전부터 안방에 굴러다니던 Kotlin In Action 책을 발견하였다. 아마도 아이들이 가져다 둔게 아닐까 싶긴 한데 눈에 띈 기념(?) 으로 계획 …
Source: junho85.pe.kr
Date Published: 4/30/2022
View: 3332
[Kotlin IN ACTION] 코틀린 기초 문법 – 코딩하는 개굴이
본 내용은 Kotlin IN ACTION (드미트리 제메로프, 스베트라나 이사코바 지음 … 코틀린도 자바처럼 한글을 변수처럼 사용할 수 있기 때문에 문자열 …
Source: yejinson97gaegul.tistory.com
Date Published: 10/11/2021
View: 2900
[kotlin] 코틀린 인 액션(1) – 길은 가면, 뒤에 있다.
문자열 템플릿 안에 $로 변수를 지정할 때 변수명 바로 뒤에 한글을 붙여서 사용하면 코틀린 컴파일러는 영문자와 한글을 한꺼번에 식별자로 인식해서 unresolved …
Source: 12bme.tistory.com
Date Published: 3/5/2022
View: 1245
공식 문서 기반의 Kotlin 한글 정리 – 곰돌푸우❤️
이 글은 https://kotlinlang.org/docs/reference/ 의 공식문서를 참고해서 한글로 차근차근 공부하며 정리한 글입니다. 아무쪼록 도움이 되시길 바라 …
Source: simsi6.tistory.com
Date Published: 12/19/2021
View: 4891
주제와 관련된 이미지 kotlin in action 한글 pdf
주제와 관련된 더 많은 사진을 참조하십시오 How to make Pdf Reader App | Show pdf file from storage | Android Studio | Kotlin. 댓글에서 더 많은 관련 이미지를 보거나 필요한 경우 더 많은 관련 기사를 볼 수 있습니다.
주제에 대한 기사 평가 kotlin in action 한글 pdf
- Author: Papermaxx
- Views: 조회수 4,823회
- Likes: 좋아요 56개
- Date Published: 2020. 8. 31.
- Video Url link: https://www.youtube.com/watch?v=ehR6lzs7SE0
Kotlin in Action – 교보문고
상품상세정보 ISBN 9791161750712 ( 1161750711 ) 쪽수 678쪽 크기 188 * 234 * 37 mm /1248g 판형알림 이 책의 원서/번역서 Kotlin in Action / Dmitry Jemerov
책소개
이 책이 속한 분야
코틀린이 안드로이드 공식 언어가 되면서 관심이 커졌다. 이 책은 코틀린 언어를 개발한 젯브레인의 코틀린 컴파일러 개발자들이 직접 쓴 일종의 공식 서적이라 할 수 있다. 코틀린 언어의 가장 큰 특징이라면 실용성을 들 수 있을 것이다. 이 책에서도 실용성을 강조하는 입장에서 쓰였다.
코틀린 기초를 소개하고, 고차함수, 객체지향, 제네릭스 등의 내용을 설명한다. 그리고 코틀린이 자바 언어를 어떻게 개선했고 기존 자바 프로젝트에서 코틀린을 함께 사용할 때 어떤 부분을 조심해야 할지를 중심으로 코틀린 언어를 설명한다. 후반에는 애노테이션과 리플렉션, DSL에 대해 실제 라이브러리 예제를 다루면서 설계 기법과 구현기법을 자세히 설명한다. 특히 이 주제는 이 책의 백미로, 다른 코틀린 책이나 공식 문서에서는 찾아보기 힘들며 실전에서 크게 도움이 받을 수 있다.
한국어판 부록에서는 코틀린 버전 1.3.3까지의 변화를 정리한 내용과 코루틴, 코틀린/JS에 대한 간단한 소개를 덧붙였다.
작가의 말
코틀린(Kotlin) 언어에 대한 아이디어는 2010년 젯브레인스(JetBrains)에서 생겼다. 그 시절 젯브레인스는 자바(Java), 자바스크립트(Javascript), C#, 파이썬(Python), 루비(Ruby) 등의 다양한 PHP 언어에 대한 개발 도구를 제공하는 널리 알려진 꽤 성공적인 회사였다. 젯브레인스의 대표 제품인 자바 IDE 인텔리J 아이디어(IntelliJ IDEA)에는 스칼라(Scala)와 그루비(Groovy) 개발을 돕는 플러그인도 들어있었다.
이렇게 다양한 언어에 대해 도구를 개발해온 경험으로 인해 우리는 프로그래밍 언어라는 영역을 전체적으로 조망할 수 있는 독특한 시야와 이해를 가질 수 있었다. 그리고 인텔리 J 를 플랫폼으로 하는 IDE들은 인텔리J 아이디어 자체를 포함해 모두 자바로 개발되고 있었다. 우리는 모던하고 강력하며 빠르게 진화하는 언어인 C#으로 개발을 진행하는 닷넷(.Net) 팀의 동료들이 너무 부러웠다. 하지만 우리는 자바를 대신할 만한 언어를 찾을 수 없었다.
우리가 자바를 대신할 언어에 대해 어떤 요구 사항을 갖고 있었을까? 처음이자 가장 분명한 요구 사항은 정적 타입 지정(static typing)이었다. 정적 타입 지정 외에 수백만 줄이나 되는 코드 베이스를 미치지 않고 개발할 수 있는 다른 방법은 없다. 둘째로 기존 자바 코드와 완전히 호환되는 언어가 필요했다. 기존 코드베이스는 젠브레인의 엄청나게 귀중한 자산이며 상호운용성이 부족해서 그런 자산을 잃어버리거나 자산의 가치가 줄어드는 일을 용납할 수는 없었다. 셋째로 그 언어를 위한 도구 개발이 쉬워야만 했다. 우리는 도구 제공 가능성을 타협하고 싶지 않았다. 회사로서 젠브레인에게 가장 중요한 가치는 개발 생산성이며 높은 생산성을 얻기 위해서는 훌륭한 도구가 필수다. 마지막으로 배우기 쉽고 뜻을 파악하기 쉬운 언어가 필요했다.
우리 회사 내부에 이렇게 충족되지 못한 수요가 있다는 사실을 발견하던 즈음에 몇몇 회사도 우리와 비슷한 처지에 있다는 사실을 알게 됐다. 따라서 젠브레인 내부의 수요를 충족할 수 있는 솔루션을 만들면 젠브레인 밖에서도 더 많은 사용자를 찾을 수 있으리라고 예상할 수 있었다. 이를 염두에 두고 우리는 새로운 언어인 코틀린을 만드는 프로젝트를 시작하기로 결정했다. 언어를 개발하는 과정에서 처음의 예상과 달리 개발 기간이 더 늘어났고 코틀린 1.0은 최초의 저장소 커밋(repository commit) 이후 5년이 넘는 시간이 지나서 나왔다. 하지만 이제는 코틀린을 원하는 사용자를 찾았다고 확신할 수 있고 그런 사용자들이 앞으로도 계속 코틀린을 사용하리라 확신한다. 코틀린은 코틀린 개발 팀이 대부분 살고 있는 러시아의 상트페테르부르크(St. Petersburg) 근처에 있는 섬 이름이다. 섬 이름을 언어 이름으로 선택하면서 우리는 자바와 실론(Ceylon) 언어의 전통을 따랐다. 하지만 자바나 실론 대신 좀 더 고향에 가까운 섬을 택하기로 결정했다.
코틀린 정식 배포가 가까워짐에 따라 우리는 코틀린 언어를 설계하는 과정에 관여하고 코틀린 언어의 특성이 왜 현재의 모습이 되었는지에 대해 자신 있게 설명할 수 있는 사람들이 쓴 책이 한 권 있다면 사람들에게 많은 도움이 되리라 생각했다. 이 책은 그런 노력의 일환이며 독자 여러분이 이 책을 통해 코틀린 언어를 더 잘 배우고 이해하기를 바란다. 여러분의 행운과 여러분이 항상 즐겁게 개발에 매진할 수 있기를 빈다!
목차
1장. 코틀린이란 무엇이며, 왜 필요한가?
__1.1 코틀린 맛보기
__1.2 코틀린의 주요 특성
____1.2.1 대상 플랫폼: 서버, 안드로이드 등 자바가 실행되는 모든 곳
____1.2.2 정적 타입 지정 언어
____1.2.3 함수형 프로그래밍과 객체지향 프로그래밍
____1.2.4 무료 오픈소스
__1.3 코틀린 응용
____1.3.1 코틀린 서버 프로그래밍
____1.3.2 코틀린 안드로이드 프로그래밍
__1.4 코틀린의 철학
____1.4.1실용성
____1.4.2간결성
____1.4.3 안전성
____1.4.4 상호운용성
__1.5 코틀린 도구 사용
____1.5.1 코틀린 코드 컴파일
____1.5.2 인텔리J 아이디어와 안드로이드 스튜디오의 코틀린 플러그인
____1.5.3 대화형 셸
____1.5.4 이클립스 플러그인
____1.5.5 온라인 놀이터
____1.5.6 자바-코틀린 변환기
__1.6요약
2장. 코틀린 기초
__2.1 기본 요소: 함수와 변수
____2.1.1 Hello, World!
____2.1.2 함수
____2.1.3 변수
____2.1.4 더 쉽게 문자열 형식 지정: 문자열 템플릿
__2.2 클래스와 프로퍼티
____2.2.1 프로퍼티
____2.2.2 커스텀 접근자
____2.2.3 코틀린 소스코드 구조: 디렉터리와 패키지
__2.3 선택의 표현과 처리: enum과 when
____2.3.1 enum 클래스 정의
____2.3.2 when으로 enum 클래스 다루기
____2.3.3 when과 임의의 객체를 함께 사용
____2.3.4 인자 없는 when 사용
____2.3.5 스마트 캐스트: 타입 검사와 타입 캐스트를 조합
____2.3.6 리팩토링: if를 when으로 변경
____2.3.7 if와 when의 분기에서 블록 사용
__2.4 대상을 이터레이션: while과 for 루프
____2.4.1 while 루프
____2.4.2 숫자에 대한 이터레이션: 범위와 수열
____2.4.3 맵에 대한 이터레이션
____2.4.4 in으로 컬렉션이나 범위의 원소 검사
__2.5 코틀린의 예외 처리
____2.5.1 try, catch, finally
____2.5.2 try를 식으로 사용
__2.6 요약
3장. 함수 정의와 호출
__3.1 코틀린에서 컬렉션 만들기
__3.2 함수를 호출하기 쉽게 만들기
____3.2.1 이름 붙인 인자
____3.2.2 디폴트 파라미터 값
____3.2.3 정적인 유틸리티 클래스 없애기: 최상위 함수와 프로퍼티
____3.3 메소드를 다른 클래스에 추가: 확장 함수와 확장 프로퍼티
____3.3.1 임포트와 확장 함수
____3.3.2 자바에서 확장 함수 호출
____3.3.3 확장 함수로 유틸리티 함수 정의
____3.3.4 확장 함수는 오버라이드 할 수 없다
____3.3.5 확장 프로퍼티
__3.4 컬렉션 처리: 가변 길이 인자, 중위 함수 호출, 라이브러리 지원
____3.4.1 자바 컬렉션 API 확장
____3.4.2 가변인자 함수: 인자의 개수가 달라질 수 있는 함수 정의
____3.4.3 값의 쌍 다루기: 중위 호출과 구조 분해 선언
__3.5 문자열과 정규식 다루기
____3.5.1 문자열 나누기
____3.5.2 정규식과 3중 따옴표로 묶은 문자열
____3.5.3 여러 줄 3중 따옴표 문자열
__3.6 코드 다듬기: 로컬 함수와 확장
__3.7 요약
4장. 클래스, 객체, 인터페이스
__4.1 클래스 계층 정의
____4.1.2 코틀린 인터페이스
____4.1.2 open, final, abstract 변경자: 기본적으로 final
____4.1.3 가시성 변경자: 기본적으로 공개
____4.1.4 내부 클래스와 중첩된 클래스: 기본적으로 중첩 클래스
____4.1.5 봉인된 클래스: 클래스 계층 정의 시 계층 확장 제한
__4.2 뻔하지 않은 생성자와 프로퍼티를 갖는 클래스 선언
____4.2.1 클래스 초기화: 주 생성자와 초기화 블록
____4.2.2 부 생성자: 상위 클래스를 다른 방식으로 초기화
____4.2.3 인터페이스에 선언된 프로퍼티 구현
____4.2.4 게터와 세터에서 뒷받침하는 필드에 접근
____4.2.5 접근자의 가시성 변경
__4.3 컴파일러가 생성한 메소드: 데이터 클래스와 클래스 위임
____4.3.1 모든 클래스가 정의해야 하는 메소드
____4.3.2 데이터 클래스: 모든 클래스가 정의해야 하는 메소드 자동 생성
____4.3.3 클래스 위임: by 키워드 사용
__4.4 object 키워드: 클래스 선언과 인스턴스 생성
____4.4.1 객체 선언: 싱글턴을 쉽게 만들기
____4.4.2 동반 객체: 팩터리 메소드와 정적 멤버가 들어갈 장소
____4.4.3 동반 객체를 일반 객체처럼 사용
____4.4.4 객체 식: 익명 내부 클래스를 다른 방식으로 작성
__4.5 요약
5장. 람다로 프로그래밍
__5.1 람다 식과 멤버 참조
____5.1.1 람다 소개: 코드 블록을 함수 인자로 넘기기
____5.1.2 람다와 컬렉션
____5.1.3 람다 식의 문법
____5.1.4 현재 영역에 있는 변수에 접근
____5.1.5 멤버 참조
__5.2 컬렉션 함수형 API
____5.2.1 필수적인 함수: filter와 map
____5.2.2 all, any, count, find: 컬렉션에 술어 적용
____5.2.3 groupBy: 리스트를 여러 그룹으로 이뤄진 맵으로 변경
____5.2.4 flatMap과 flatten: 중첩된 컬렉션 안의 원소 처리
__5.3 지연 계산 lazy 컬렉션 연산
____5.3.1 시퀀스 연산 실행: 중간 연산과 최종 연산
____5.3.2 시퀀스 만들기
__5.4 자바 함수형 인터페이스 활용
____5.4.1 자바 메소드에 람다를 인자로 전달
____5.4.2 SAM 생성자: 람다를 함수형 인터페이스로 명시적으로 변경
__5.5 수신 객체 지정 람다: with와 apply
____5.5.1 with함수
____5.5.2 apply함수
__5.6 요약
6장. 코틀린 타입 시스템
__6.1 널 가능성
____6.1.1 널이 될 수 있는 타입
____6.1.2 타입의 의미
____6.1.3 안전한 호출 연산자: “?.”
____6.1.4 엘비스 연산자 “?:”
____6.1.5 안전한 캐스트: as?
____6.1.6 널 아님 단언: !!
____6.1.7 let함수
____6.1.8 나중에 초기화할 프로퍼티
____6.1.9 널이 될 수 있는 타입 확장
____6.1.10 타입 파라미터의 널 가능성
____6.1.11 널 가능성과 자바
__6.2 코틀린의 기본 타입
____6.2.1 기본 타입: Int, Boolean 등
____6.2.2 널이 될 수 있는 기본 타입: Int?, Boolean? 등
____6.2.3 숫자 변환
____6.2.4 Any, Any?: 최상위 타입
____6.2.5 Unit 타입: 코틀린의 void
____6.2.6 Nothing 타입: “이 함수는 결코 정상적으로 끝나지 않는다.”
__6.3 컬렉션과 배열
____6.3.1 널 가능성과 컬렉션
____6.3.2 읽기 전용과 변경 가능한 컬렉션
____6.3.3 코틀린 컬렉션과 자바
____6.3.4 컬렉션을 플랫폼 타입으로 다루기
____6.3.5 객체의 배열과 기본 타입의 배열
__6.4요약
2부. 코틀린답게 사용하기
7장. 연산자 오버로딩과 기타 관례
__7.1 산술 연산자 오버로드
____7.1.1 이항 산술 연산 오버로딩
____7.1.2 복합 대입 연산자 오버로딩
____7.1.3 단항 연산자 오버로딩
__7.2 비교 연산자 오버로딩
____7.2.1 동등성 연산자: “equals”
____7.2.2 순서 연산자: compareTo
____7.3 컬렉션과 범위에 대해 쓸 수 있는 관례
____7.3.1 인덱스로 원소에 접근: get과 set
__7.3.3 in관례
____7.3.3 rangeTo관례
____7.3.4 for 루프를 위한 iterator 관례
__7.4 구조 분해 선언과 component 함수
____7.4.1 구조 분해 선언과 루프
__7.5 프로퍼티 접근자 로직 재활용: 위임 프로퍼티
____7.5.1 위임 프로퍼티 소개
____7.5.2 위임 프로퍼티 사용: by lazy()를 사용한 프로퍼티 초기화 지연
____7.5.3 위임 프로퍼티 구현
____7.5.4 위임 프로퍼티 컴파일 규칙
____7.5.5 프로퍼티 값을 맵에 저장
____7.5.6 프레임워크에서 위임 프로퍼티 활용
__7.6 요약
8장. 고차 함수: 파라미터와 반환 값으로 람다 사용
__8.1 고차 함수 정의
____8.1.1 함수 타입
____8.1.2 인자로 받은 함수 호출
____8.1.3 자바에서 코틀린 함수 타입 사용
____8.1.4 디폴트 값을 지정한 함수 타입 파라미터나 널이 될 수 있는 함수 타입 파라미터
____8.1.5 함수를 함수에서 반환
____8.1.6 람다를 활용한 중복 제거
__8.2 인라인 함수: 람다의 부가 비용 없애기
____8.2.1 인라이닝이 작동하는 방식
____8.2.2 인라인 함수의 한계
____8.2.3 컬렉션 연산 인라이닝
____8.2.4 함수를 인라인으로 선언해야 하는 경우
____8.2.5 자원 관리를 위해 인라인된 람다 사용
__8.3 고차 함수 안에서 흐름 제어
____8.3.1 람다 안의 return문: 람다를 둘러싼 함수로부터 반환
____8.3.2 람다로부터 반환: 레이블을 사용한 return
____8.3.3 무명 함수: 기본적으로 로컬 return
__8.4 요약
9장. 제네릭스
__9.1 제네릭 타입 파라미터
____9.1.1 제네릭 함수와 프로퍼티
____9.1.2 제네릭 클래스 선언
____9.1.3 타입 파라미터 제약
____9.1.4 타입 파라미터를 널이 될 수 없는 타입으로 한정
__9.2 실행 시 제네릭스의 동작: 소거된 타입 파라미터와 실체화된 타입 파라미터
____9.2.1 실행 시점의 제네릭: 타입 검사와 캐스트
____9.2.2 실체화한 타입 파라미터를 사용한 함수 선언
____9.2.3 실체화한 타입 파라미터로 클래스 참조 대신
____9.2.4 실체화한 타입 파라미터의 제약
__9.3 변성: 제네릭과 하위 타입
____9.3.1 변성이 있는 이유: 인자를 함수에 넘기기
____9.3.2 클래스, 타입, 하위 타입
____9.3.3 공변성: 하위 타입 관계를 유지
____9.3.4 반공변성: 뒤집힌 하위 타입 관계
____9.3.5 사용 지점 변성: 타입이 언급되는 지점에서 변성 지정
____9.3.6 스타 프로젝션: 타입 인자 대신 * 사용
__9.4 요약
10장. 애노테이션과 리플렉션
__10.1 애노테이션 선언과 적용
____10.1.2 애노테이션 대상
____10.1.3 애노테이션을 활용한 JSON 직렬화 제어
____10.1.4 애노테이션 선언
____10.1.5 메타애노테이션: 애노테이션을 처리하는 방법 제어
____10.1.6 애노테이션 파라미터로 클래스 사용
____10.1.7 애노테이션 파라미터로 제네릭 클래스 받기
__10.2 리플렉션: 실행 시점에 코틀린 객체 내부 관찰
____10.2.1 코틀린 리플렉션 API: KClass, KCallable, KFunction, KProperty
____10.2.2 리플렉션을 사용한 객체 직렬화 구현
____10.2.3 애노테이션을 활용한 직렬화 제어
____10.2.4 JSON 파싱과 객체 역직렬화
____10.2.5 최종 역직렬화 단계: callBy(), 리플렉션을 사용해 객체 만들기
__10.3 요약
11장. DSL 만들기
__11.1 API에서 DSL로
____11.1.1 영역 특화 언어라는 개념
____11.1.2 내부 DSL
____11.1.3 DSL의 구조
____11.1.4 내부 DSL로 HTML 만들기
__11.2 구조화된 API 구축: DSL에서 수신 객체 지정 DSL 사용
____11.2.1 수신 객체 지정 람다와 확장 함수 타입
____11.2.2 수신 객체 지정 람다를 HTML 빌더 안에서 사용
____11.2.3 코틀린 빌더: 추상화와 재사용을 가능하게 하는 도구
__11.3 invoke 관례를 사용한 더 유연한 블록 중첩
____11.3.1 invoke 관례: 함수처럼 호출할 수 있는 객체
____11.3.2 invoke 관례와 함수형 타입
____11.3.3 DSL의 invoke 관례: 그레이들에서 의존관계 정의
__11.4 실전 코틀린 DSL
____11.4.1 중위 호출 연쇄: 테스트 프레임워크의 should
____11.4.2 기본 타입에 대한 확장 함수 정의: 날짜 처리
____11.4.3 멤버 확장 함수: SQL을 위한 내부 DSL
____11.4.4 안코: 안드로이드 UI를 동적으로 생성하기
__11.5 요약
부록 A. 코틀린 프로젝트 빌드
__A.1 그레이들로 코틀린 코드 빌드
__A.2 메이븐으로 코틀린 프로젝트 빌드
__A.3 앤트로 코틀린 코드 빌드
부록 B. 코틀린 코드 문서화
__B.1 코틀린 문서화 주석 작성
__B.2 API 문서 생성
부록 C. 코틀린 에코시스템
__C.1 테스팅
__C.2 의존관계 주입
__C.3 JSON 직렬화
__C.4 HTTP 클라이언트
__C.5 웹 애플리케이션
__C.6 데이터베이스 접근
__C.7 유틸리티와 데이터 구조
__C.8 데스크탑 프로그래밍
부록 D. 코틀린 1.1과 1.2, 1.3 소개
__D.1 코틀린 1.1
__D.2 코틀린 1.2
__D.3 코틀린 1.3
부록 E. 코루틴과 Async/Await
__E.1 코루틴이란?
__E.2 코틀린의 코루틴 지원: 일반적인 코루틴
__E.3 suspend 키워드와 코틀린의 일시 중단 함수 컴파일 방법
__E.4 코루틴 빌더 만들기
__E.5 결론
부록 F. 코틀린/JS
__F.1 코틀린/JS 프로젝트 기본 설정
__F.2 Hello, World!
__F.3 코틀린에서 DOM에 접근
__F.4 js() 함수와 dynamic 타입, external 변환자
__F.5 자바스크립트에서 코틀린 호출
__F.6 복잡한 예제: HTML 빌더, jQuery 라이브러리 사용 예제
__F.7 외부 라이브러리에 대한 코틀린/JS 래퍼
__F.8 결론
추천사
안드레이 브레스라프(젠브레인 코틀린 리드 디자이너) 언어를 맨 밑바닥부터 설계하는 일은 그 자체로도 힘든 일이다. 하지만 새 언어가 기존의 다른 언어와 잘 어우러지도록 설계하는 일은 전혀 다르다. 기존 언어와 어우러지는 언어를 설계하는 과정에서 어두컴컴하고 스산한 괴물 소굴을… 더보기
이 추천의 글을 쓰는 동안에도 두 가지 타겟 플랫폼을 더 개발 중이다. 이제 코틀린은 자바스크립트를 지원하기 때문에 풀 스택 웹 개발이 가능하다. 또한 빠른 시일 안에 필요에 따라 네이티브 코드로 코틀린을 컴파일해서 VM없이도 코틀린 프로그램을 실행할 수 있게 할 예정이다. 따라서 현재 이 책은 JVM에 주로 초점을 맞추지만 앞으로는 이 책에서 배운 내용을 여러 다른 실행 환경에 적용할 수 있을 것이다.
저자들은 초기부터 코틀린 팀의 일원이었기 때문에 코틀린 언어와 그 내부 구조에 대해 잘 알고 있다. 그들은 각종 컨퍼런스나 워크숍 발표, 코틀린 교육 과정 등을 통해 코틀린을 더 잘 설명할 수 있게 됐고, 사람들이 자주 하는 질문이나 흔히 저지르는 실수에 대해 더 많이 알게 됐다. 이 책은 코틀린 언어의 특성을 뒷받침하는 고수준 개념을 설명하면서도 독자들에게 필요한 만큼 각 특성의 세부 사항을 제시한다.
나는 독자 여러분이 이 책을 읽으면서 코틀린과 함께 즐거운 시간을 보내기 바란다. 내가 종종 코틀린 커뮤니티에 포스팅하면서 쓰는 인사말로 추천의 글을 마친다. 코틀린과 함께 좋은 시간 되세요! 언어를 맨 밑바닥부터 설계하는 일은 그 자체로도 힘든 일이다. 하지만 새 언어가 기존의 다른 언어와 잘 어우러지도록 설계하는 일은 전혀 다르다. 기존 언어와 어우러지는 언어를 설계하는 과정에서 어두컴컴하고 스산한 괴물 소굴을 여럿 통과하면서 수없이 많은 힘센 괴물을 물리쳐야 한다. 자바 상호운용성은 코틀린의 가장 중요한 주춧돌이라 할 수 있으며, 이 책은 자바 코틀린 상호운용성에 신경을 많이 썼다. 이미 존재하는 자바 프로젝트에 코틀린을 점진적으로 도입할 때 자바 코틀린 상호운용성이 매우 중요하다. 심지어 새로운 프로젝트를 코틀린만으로 작성하더라도 자바로 쓰인 광범위한 라이브러리로 이뤄진 플랫폼이라는 더 큰 그림에 들어맞게 코틀린 언어를 구사해야 한다.이 추천의 글을 쓰는 동안에도 두 가지 타겟 플랫폼을 더 개발 중이다. 이제 코틀린은 자바스크립트를 지원하기 때문에 풀 스택 웹 개발이 가능하다. 또한 빠른 시일 안에 필요에 따라 네이티브 코드로 코틀린을 컴파일해서 VM없이도 코틀린 프로그램을 실행할 수 있게 할 예정이다. 따라서 현재 이 책은 JVM에 주로 초점을 맞추지만 앞으로는 이 책에서 배운 내용을 여러 다른 실행 환경에 적용할 수 있을 것이다.저자들은 초기부터 코틀린 팀의 일원이었기 때문에 코틀린 언어와 그 내부 구조에 대해 잘 알고 있다. 그들은 각종 컨퍼런스나 워크숍 발표, 코틀린 교육 과정 등을 통해 코틀린을 더 잘 설명할 수 있게 됐고, 사람들이 자주 하는 질문이나 흔히 저지르는 실수에 대해 더 많이 알게 됐다. 이 책은 코틀린 언어의 특성을 뒷받침하는 고수준 개념을 설명하면서도 독자들에게 필요한 만큼 각 특성의 세부 사항을 제시한다.나는 독자 여러분이 이 책을 읽으면서 코틀린과 함께 즐거운 시간을 보내기 바란다. 내가 종종 코틀린 커뮤니티에 포스팅하면서 쓰는 인사말로 추천의 글을 마친다. 코틀린과 함께 좋은 시간 되세요! 닫기
출판사 서평
Kotlin in Action #1. 코틀린 시작하기
‘Kotlin in Action’ 책을 학습하고 정리한 내용입니다.
1. 코틀린 시작하기
코틀린은 자바 플랫폼에서 돌아가는 새로운 프로그래밍 언어이다. 자바 코드와의 상호운용성이 가능하며 간결하고 실용적이다. 안드로이드 앱 뿐만 아니라 서버 개발에서도 사용 할 수 있다.
코틀린 코드 맛보기
이번 예제에서는 Person 클래스를 정의하고, 여러 Person 을 모아둔 컬렉션을 만들어서 가장 나이가 많은 사람을 찾아 결과를 출력해보는 코드를 작성해본다.
// Person 데이터 클래스 data class Person(val name: String, val age: Int ?= null) fun main(args: Array
) { // Person 컬렉션 생성 val persons = listOf( Person(“jayden”, 29), Person(“ella”, 28) ) // 람다, 엘비스 연산자를 통해 가장 나이가 많은 Person 필터 val oldest = persons.maxBy { it.age?: 0 } // 문자열 템플릿으로 결과 출력 println(“나이가 가장 많은 사람: $oldest”) } 결과 출력 화면
나이가 가장 많은 사람: Person(name=jayden, age=29)
위 코드를 하나씩 살펴보면서 코틀린 특징에 대해 간단하게 알아보자.
코틀린에서는 값을 담는 클래스를 생성할 때, data 키워드를 붙여서 생성한다. Person 데이터 클래스는 name, age 프로퍼티를 갖고 있다. age 경우에는 ? 키워드를 사용해서 널이 될 수 있는 타입을 명시하고 있다. 그리고 프로퍼티의 디폴트 값으로 null을 지정하고 있다.
특정 값을 기준으로 가장 나이가 많은 사람을 추출하기 위해서 maxBy 함수를 사용하고 있다. maxBy 함수에 전달한 람다 식은 파라미터 하나를 받고 있고, it 이라는 이름을 사용하여 age 프로퍼티에 접근한다. 이 때, age 프로퍼티가 null 값일수도 있으므로 ?: 엘비스 연산자를 사용해서 null인 경우 0을 반환한다.
코틀린 주요 특성
코틀린 대상 플랫폼
코틀린은 자바가 사용되고 있는 모든 용도에 적합하므로 같이 사용하거나 자바 코드를 대체할 수 있다. 자바 코드는 이미 네이버, 카카오, 11번가 등과 같이 대기업에서도 사용하는 안정적인 언어이지만, 그럼에도 불구하고 코틀린을 사용해야 하는 이유는 자바 코드보다 간결하고 생산적이며 안전한 언어이기 때문이다.
코틀린을 일반적으로 활용할 수 있는 영역은 다음과 같다.
서버 코드 (웹 애플리케이션 백엔드 코드)
안드로이드 앱 코드
이외에도 iOS, 데스크탑 애플리케이션, 노드, 브라우저에서도 실행할 수 있다.
정적 타입 지정 언어
자바와 마찬가지로 코틀린도 정적 타입 지정 언어이다. 정적 타입 지정이란 프로그램 구성 요소의 타입을 컴파일 시점에 알 수 있기 때문에 컴파일러가 타입을 검증해주는 안정성을 보장 받을 수 있다.
동적 타입 지정 언어에서는 타입과 관계없이 모든 값을 변수에 넣을 수 있고, 검증을 컴파일 시점이 아닌 실행 시점에 이루어진다. 동적 타입 지정이기 때문에 유연하게 사용할 수 있는 장점이 있을 수 있다. 다만, 컴파일 시점이 아닌 실행 시점에 오류를 발견한다는 점은 실제 운영되는 애플리케이션을 만들 때 안정성이 떨어질 수 있는 문제점이 있다.
코틀린 변수 지정한 코드를 보면 다음과 같다.
// 코틀린은 변수 x가 Int 타입이라는 것을 문맥을 통해 유추한다 var x = 1
정적 타입 지정 언어라고 했는데 마치 자바스크립트와 유사해보이기 때문에 동적 타입 지정 언어라고 오해할 수 있다. 코틀리엔서는 모든 변수 타입을 프로그래머가 직접 명시하지 않더라도 컴파일러가 문맥으로부터 변수 타입을 자동으로 유추하기 때문에 타입 선언을 생략해도 된다. 자바 10에서도 코틀린처럼 타입을 명시적으로 선언하지 않아도 컴파일러가 유추한다.
컴파일러가 문맥을 파악해서 변수 타입을 결정하는 것을 타입 추론 이라고 한다.
코틀린 철학
코틀린은 자바와 비교해서 실용적이고 간결하며 안전한 언어를 장점을 내세운다 실용성, 간결성, 안정성, 상호운용성 각각의 장점에 대해 자세히 살펴보자.
실용성
코틀린은 실제 문제를 해결하기 위해 만들어진 실용적인 언어이다. 다른 최신 언어 설계나 연구중인 아이디어를 코틀린을 통해 탐구하려 하지 않고, 다른 프로그래밍 언어가 채택한 이미 입증된 해법과 기능에 의존한다. 이로인해 언어의 복잡도가 줄고 흔히 알고 있는 개념을 통해 쉽게 코틀린 언어를 배울 수 있다.
자바를 기존에 사용했다면 자바 언어 스타일이나 기법을 활용할 수 있고, 나중에는 코틀린만의 강력한 특징을 배워서 이를 코드에 녹여내서 더 간결한 코드를 작성할 수 있다.
실용성에 있어서 다른 측면으로 편리한 개발 도구 및 환경이 생산성 향상에 필수 요소이다. 자바 개발자라면 많이 사용하고 있는 인텔리J의 경우에 코틀린 코드를 작성하면 IDE 차원에서 작성된 코드를 감지하고 더 좋은 패턴을 추천한다. 이는 자바 개발할 때 경험할 수 있는 부분이다. 예를 들어, 절차형으로 작성한 부분을 람다식으로 변경하라고 코드에서 추천해주고 기존 코드를 변경해준다.
간결성
개발자는 새로운 코드를 작성하는 시간만큼 기존에 작성된 코드를 파악하고 유지보수 하는 업무를 하게 된다. 코틀린으로 작성하게 되면 장황한 코드를 줄이고 간결하게 작성할 수 있으므로 코드를 파악하기가 더 수월하다. 파악하기 쉽다는 것은 단순히 코드 라인 수가 적은것 뿐만 아니라 코드 자체에서 쉽게 의도를 파악할 수 있는 구문 구조를 제공하는지 또는 실제 의미를 이해하기 위해서 방해가 되는 부가적인 코드가 적은지이다.
자바에서 게터, 세터, 생성자 파라미터를 쉽게 만들기 위해서 롬복 라이브러리를 사용했었다. 코틀린에서는 이러한 로직을 묵시적으로 제공하기 때문에 준비 코드로 인해 지저분해지는 일은 없다. 이러한 기능은 C#에서도 제공하는 기능이다.
안정성
프로그래밍에서 안전하는 말은 프로그램에서 발생하는 일부 유형의 오류를 프로그램 설계가 원천적으로 방지해준다는 뜻이다. 물론 절대적이지 않다. 컴파일러에게 프로그램이 어떻게 동작하는지에 대한 자세한 코드를 작성하고 제공함으로써 작동 의도에 대한 일치를 검증할 수 있다. 하지만 더 많은 정보를 붙임으로써 생산성이 하락하는 것을 감수해야 한다.
코틀린은 오랜시간동안 검증 받은 JVM 위에서 동작하기 때문에 안정성을 달성하되 자바보다 더 작은 비용을 지불하며 견고한 프로그램을 만들기 위해 설계되었다. 특히 코틀린은 NullPointerException(NPE) 에러를 방지하기 위해서 노력한다.
코틀린의 타입 시스템은 null이 될 수 있는 값과 될 수 없는 값을 정의하며 컴파일 시점에 체크해준다. 특정 타입이 null 값이 될 수 있는 여부를 표시하기 위해서 ? 글자를 추가한다.
val s: String? = null // null 허용함 val s: String = “” // null 허용하지 않음
그리고 어떤 객체를 다른 타입으로 캐스팅 하기 전에 타입을 미리 검사하지 않으면 ClassCastException이 발생할 수 있다. 코틀린에서는 타입 검사와 캐스팅이 한 연산자에 의해 이루어지기 때문에 더 안전한다.
if (value is String) // 타입 검사 println(value.toUpperCase())
상호운용성
코틀린에서 기존 자바에서 사용하던 라이브러리, 클래스, 상속, 인터페이스, 구현, 애노테이션 등을 그대로 사용할 수 있다. 다른 JVM 언어와 달리 코틀린은 자바 코드를 아무런 노력 없이 호출할 수 있고, 반대로 자바에서도 코틀린 코드를 호출할 수 있다. 이에 따라 자바와 코틀린 코드를 하나의 프로젝트에서 섞어 쓸 수 있다.
이러한 장점 때문에 기존 프로젝트에 작성된 자바 코드를 그대로 가져가며 일정 부분을 코틀린으로 변경할 수 있다. 그리고 인텔리J와 같은 도구를 사용하면 도구 자체에서 자바 코드를 코틀린 코드로 컨버팅하는 기능을 제공해준다. 이 기능은 완벽하게 대체해주지는 않지만 프로그래머가 해야 하는 일정 부분의 수고스러움을 덜어 준다는 점에 있어서 편하다.
코틀린 도구 사용
자바와 마찬가지로 코틀린도 컴파일 언어이다. 자바는 .java 파일을 컴파일해서 .class 파일을 생성한다. 그리고 .class 파일을 JVM 위에서 실행한다. 코틀린에서는 컴파일 과정이 어떻게 이뤄지는지와 그 과정에서 어떤 도구가 쓰여지는지 알아보자.
코틀린 코드 컴파일
코틀린 소스코드를 저장할 때는 .ks 확장자를 파일에 붙인다. 코틀린 컴파일러는 소스코드를 분석해서 .class 파일을 생성한다. 생성된 .class 파일은 개발 중인 애플리케이션에 맞는 표준 패키징 과정을 거쳐 실행될 수 있다.
간단한 컴파일 방식은 커맨드라인에서 kotlinc 명령을 통해 할 수 있다. 컴파일하고 나서 java 명령으로 그 코드를 실행한다. 코틀린 컴파일러로 컴파일한 코드는 코틀린 런타임 라이브러리에 의존한다. 이 라이브러리에는 코틀린 자체 표준 라이브러리 클래스와 자바 API의 기능을 확장한 내용이 있다. 코틀린 컴파일한 애플리케이션을 배포할 때는 런타임 라이브러리도 함께 배포해야 한다.
자바에서 사용한 빌드 도구로는 메이븐과 그레이들, 앤트 등이 있다. 이러한 빌드 도구는 애플리케이션을 패키징 할 때, 알아서 코틀린 라이브러리를 포함시켜준다.
코틀린 플레이그라운드
프로그램 설치 없이 코틀린 언어를 배우기 위한 가장 쉬운 방법은 코틀린에서 제공하는 코틀린 플레이그라운드를 이용하면 된다. 웹 브라우저에서 코틀린 코드를 작성, 컴파일, 실행을 편하게 할 수 있다.
요약
코틀린은 타입 추론을 지원하는 정적 타입 지정 언어이다.
객체지향과 함수형 프로그래밍 스타일 모두를 지원한다.
서버 애플리케이션, 안드로이드 등 다양한 대상한 플랫폼에서 활용 가능하다.
코틀린은 무료이며 오픈소스이다. 주요 IDE와 빌드 시스템을 지원한다.
코틀린은 실용적이며, 안전하고, 간결하며 상호운용성이 좋다.
Kotlin In Action 책을 읽자
반응형
왜인지 며칠전부터 안방에 굴러다니던 Kotlin In Action 책을 발견하였다. 아마도 아이들이 가져다 둔게 아닐까 싶긴 한데 눈에 띈 기념(?) 으로 계획을 세워서 책을 좀 읽어 볼까 한다.
목차는 다음과 같다.
1부 코틀린 소개 1장 코틀린이란 무엇이며, 왜 필요한가? 2장 코틀린 기초 3장 함수 정의와 호출 4장 클래스, 객체, 인터페이스 5장 람다로 프로그래밍 6장 코틀린 타입 시스템
2부 코틀린답게 사용하기 7장 연산자 오버로딩과 기타 관례 8장 고차 함수: 파라미터와 반환 값으로 람다 사용 9장 제네릭스 10장 애노테이션과 리플렉션 11장 DSL 만들기
일주일에 1장 (Chapter) 정도를 목표로 읽으면 2달 반 정도면 다 읽을 수 있지 않을까 생각된다. 여력이 되면 개발자들과 스터디도 해 봤으면 한다.
개인적으로 Kotlin 은 앞으로 크게 성장할 언어라고 생각하고 있다. 특히 Java 가 지금보다 더 분발하지 않으면 Java 의 영역은 크게 위협 받으리라 생각한다. Scala 나 Clojure 같은 언어들도 기대되는 언어들이긴 하지만 대중화가 되기에는 현재 기준으로는 좀 어려운 편이다. 비교적 쉽게 배울 수 있는 Kotlin 이 더 빠르게 대중화 될 것이라 기대된다. IntelliJ 로 유명한 JetBrains 에서 만든 언어다 보니 개발툴 지원도 좋고 (심지어 Java 코드를 쉽게 Kotlin 으로 변환도 해줌), 안드로이드 공식 언어, 스프링프레임워크 지원 등은 Kotlin 을 빠르게 성장 시키는데 큰 영향을 줄 것이다.
얼마전 만났던 어떤 개발자 지원자는 코딩테스트를 코틀린으로 풀어서 제출 했었다. 점점 그런 케이스를 많이 보게 되지 않을까 생각된다.
반응형
[Kotlin IN ACTION] 코틀린 기초 문법
반응형
본 내용은 Kotlin IN ACTION (드미트리 제메로프, 스베트라나 이사코바 지음 / 에이콘 출판사) 책을 기반으로 작성되었습니다.
Kotlin IN ACTION 2강 : 코틀린 기초
Hello World!
fun main(args: Array
) { println(“Hello World!”) } 해당 코드에서 코틀린의 특징을 알아보자.
함수를 선언 시에, fun 키워드를 사용한다.
파라미터 이름 뒤에 파라미터의 타입을 쓴다. (변수 선언 시에도 마찬가지)
함수를 최상위 수준에 정의할 수 있다. (클래스 안에 함수를 넣어야 할 필요가 없다.)
배열 처리를 위한 문법이 따로 존재하지 않는다.
출력 시 println을 사용한다. (표준 자바 라이브러리 함수를 간결하게 사용 할 수 있도록 감싼 wrapper 를 제공하기 때문에 가능한 것)
세미 콜론을 붙이지 않아도 된다.
함수
fun max (a: Int, b: Int): Int{ return if (a > b) a else b }
해당 식에서 코틀린의 특징을 알아보자.
함수 선언 키워드인 fun 뒤에는 함수 이름이 온다.
함수 이름 뒤에는 괄호 안에 파라미터 목록이 온다.
함수의 반환 타입은 파라미터 목록 다음에 콜론(:)으로 구분 후 다음에 온다.
코틀린에서의 if 는 식이다.
해당 코드는 본문이 중괄호로 둘러싸인 함수 즉, 블록이 본문인 함수 이나, 등호화 식으로 이루어진 식이 본문인 함수 로 변환할 수 있다. 코틀린에서는 아래와 같은 형태로, if, when, try 등에도 사용한다. 식이 본문인 함수에 한해서 반환 타입을 생략 가능하다.
이나, 등호화 식으로 이루어진 로 변환할 수 있다.
fun max(a: Int, b: Int): Int = if (a > b) a else b //반환 타입 생략 가능 fun max(a: Int, b: Int) = if (a > b) a else b
식과 문
자바에서 주로 if 를 표현 시 if문이라고 배웠을 것이다. 그러나, 코틀린에서의 if는 식에 해당한다. 둘은 각각 정확히 무엇인가?
식 : 값을 만들어 내며 , 다른 식의 하위 요소로 계산에 참여할 수 있다.
, 다른 식의 하위 요소로 계산에 참여할 수 있다. 문 : 자신을 둘러써고 잇는 가장 안쪽 블록의 최상위 요소로 존재하며, 아무런 값을 만들어 내지 않는다.
코틀린에서의 식 : 코틀린에서는 루프를 제외한 대부분의 제어 구조가 식에 해당한다.
변수
val doraemonHeight = 129.3 //타입을 명시해도 된다 val doraemonHeight: Double = 129.3
타입을 지정하지 않을 시, 컴파일러가 초기화 식을 분석하여 초기화 식의 타입을 변수 타입으로 지정
val : 변경 불가능한 참조를 저장하는 변수로, 초기화하고나면 재대입이 불가한 final 에 해당하는 변수 기본적으로 모든 변수를 val 로 선언해 사용하고 꼭 필요할 때만 var 을 사용할 것을 권장 참조 자체는 변경될 수 없지만 참조가 가리키는 객체의 내부 값은 변경될 수 있다. (예시 > array 추가 등)
var : 변경 가능한 참조로, 일반 변수에 해당 변수의 값을 변경할 수는 있지만 변수의 타입을 변경할 수는 없다
문자열 형식 지정
Hello World 예제를 사용자의 이름을 받아 인사를 출력하는 프로그램을 구현해 보자.
fun main(args: Array
) { val name = if (args.size > 0) args[0] else “World” //만일 사용자가 인자를 넘기지 않았다면 Hello Wordl!을 출력한다. println(“Hello $name!”) //문자열 템플릿 } //중괄호를 이용해 문자열 템플릿에 식을 넣어 더 간략하게 사용 가능 fun main(args: Array ) { println(“Hello ${if (args.size > 0) args[0] else “World”}!”) //문자열 템플릿 } 문자열 템플릿 : $을 앞에 붙여 변수를 문자열 안에 사용할 수 있다 컴파일러는 각 식을 정적으로 컴파일 시점에 검사하며, 이때 존재하지 않는 변수가 발견될 경우 컴파일 오류를 발생시킨다. 참고 : $을 문자열에 넣고 싶을 경우 \을 앞에 붙인다. 중괄호로 둘러 쌓아 식의 형식을 넣을 수 있다.코틀린도 자바처럼 한글을 변수처럼 사용할 수 있기 때문에 문자열 템플릿 사용 시 ‘$name님이 접속하였습니다.’ 와 같은 형태 사용 시 영문자와 한글을 동시에 인식하여 ‘name님’ 을 변수로 인식할 수 있다. 따라서, 식이 아닌 일반 변수이 더라도 ‘${name}’의 형태로 쓰는 습관 을 가지자.
클래스
자바에서의 클래스와 코틀린의 클래스 선언 형태를 비교해보자.
class Doraemon { private double height; public Doraemon(double height) { this.height = height; } public double getHeight() { return height; } }
자바에서는 클래스의 필드가 늘어날수록 생성자의 파라미터와 getter/setter가 점점 늘어나게 된다.
그에 비해 Kotlin은 간단하게 해결이 가능하다.
class Doraemon(val height: Double) //정말 이게 끝이다. 어메이징 😀
java와 달리 클래스의 접근자 public이 생략되었는데, 코틀린은 기본 가시성이 java와 달리 public이므로, 생략해도 된다.
프로퍼티
클래스의 개념은 데이터를 캡슐화 하는 것에 있다. 자바에서는 필드와 접근자(getter/setter)를 한데 묶어 프로퍼티라고 부르나, 코틀린은 프로퍼티를 언어의 기본으로 제공하고 선언 시에 변수와 마찬가지로 val(읽기 전용) 이나 var(변경도 가능)을 사용한다.
class Doraemon( val height: Double, var weight: Double, ) … //사용 val miniDora = Doraemon(129.3, 129.3) println(miniDora.height) miniDora.weight = 132.9 //변경 가능한 프로퍼티의 경우 자바의 세터 대신 위와 같이 사용한다. println(miniDora.weight)
커스텀 접근자 : 별도의 필드에 저장할 필요가 없이 구현을 제공하는 getter만 존재하며, 프로퍼티에 접근할 때마다 getter가 프로퍼티 값을 매번 다시 계산한다.
class Doraemon( val height: Double, var weight: Double, ) { val doreamonBMI : Double get(){ return weight/(height * height) } } … //사용 val miniDora = Doraemon(129.3, 129.3) println(miniDora.doraemonBMI)
코틀린 소스 코드 구조
기본적인 import는 자바와 유사하다. 하위 정의된 함수와 프로퍼티를 구체적으로 import 가능하나, 상위를 import 하여 사용해도 컴파일에 문제가 없다. 코틀린에서는 클래스 뿐 아니라 함수와 프로퍼티까지 단위로 임포트 가능하다.
위의 코드를 예로 들어, package comics.doreamon 에 fun getTools 라는 함수가 존재한다고 하면, import comics.doraemon.getTools 를 하면 해당 함수만 import 가능하다.
자바에서는 디렉터리 구조가 패키지 구조를 그대로 따라야 하여, comics package 아래 doraemon 패키지가 존재해야만 하나, 코틀린에서는 여러 클래스를 한 파일에 넣을 수 있고 이름도 자유롭게 정할 수 있기 때문에 패키지 구조와 디렉터리 구조가 일치해야 할 필요가 없다. 여러 클래스를 한 파일에 넣는 것을 주저하지 않으면서, 일반적으로 자바의 방식대로 패키지 별로 디렉터리를 구성할 것을 권장한다.
enum 클래스 정의
enum class Tools { //enum class 를 사용해야 한다. POCKET, DOOR, COPTER, LIGHT, TIMEMACHINE } //위와 같이 단순히 열거할 수도 있으나, 클래스 안에 프로퍼티나 메소드를 정의할 수 있다. enum class Tool(val name: String, val id: Int) { POCKET(“주머니”, 0), DOOR(“어디로든 문”, 1), COPTER(“대나무 헬리콥터”,2), LIGHT(“스몰라이트”, 3), TIMEMACHINE(“타임머신”, 4); //끝나는 부분에 반드시 세미콜론을 사용해야 한다. fun getInfo() = “[ID : ${id}] ${name}” }
코틀린에서 유일하게 세미콜론을 필수로 넣어야 한다. enum클래스 안에 메소드를 정의하는 경우는 반드시 상수목록과 메소드 정의 사이에 구분을 위해 세미콜론을 넣어야 한다.
When
자바에서의 switch 에 해당한다.
if 와 마찬가지로 식이며, 식이 본문인 함수에 바로 사용할 수 있다.
자바와 같이 한 분기에 여러 식을 사용할 수도 있다.
임의의 객체 형태 또한 허용하여 switch 보다 강력하다.
각 분기의 조건이 boolean 결과를 계산하는 식이라면 when 에 인자가 없어도 된다.
import example.colors.Color fun checkWarmColor(c: Color) = when (c) { RED, ORANGE, YELLOW -> true BLUE, BLACK -> false } fun mixColor(c1: Color, c2: Color) = when (setOf(c1, c2)) { setOf(RED, YELLOW) -> ORANGE setOf(YELLOW, BLUE) -> GREEN setOf(RED, BLUE) -> PURPLE else -> throw Exception(“Color Not Supported”) } //동일한 코드를 아래와 같이도 구현 가능하다. fun mixColor(c1: Color, c2: Color) = when { (c1 == RED && c2 == YELLOW) || (c1 == YELLOW && c2 == RED) -> ORANGE (c1 == YELLOW && c2 == BLUE) || (c1 == BLUE && c2 == YELLOW) -> GREEN (c1 == RED && c2 == BLUE) || (c1 == BLUE && c2 == RED) -> PURPLE else -> throw Exception(“Color Not Supported”) }
타입 캐스팅
원하는 타입으로 명시적 타입 캐스팅 시 as 키워드를 사용한다.
val value = v as Num
원하는 타입이 맞는지 확인 시 is 키워드를 사용한다.
fun eval (e: Expr) : Int = if(e is Num) { e.value } else if (e is Sum) { eval(e.right)+ eval(e.left) } //when 으로 변경 fun eval (e: Expr) : Int = when (e) { is Num -> e is Num e.value is Sum -> e is Sum eval(e.right)+ eval(e.left) }
이터레이션
while
while(조건) { //해당 조건이 참인 동안 반복 실행한다. } do { //맨 처음에 무조건 한번 실행 한 후, 조건이 참인 동안 반복 실행한다. } while(조건)
for
for (i in 1..100) { print(“count : ${i}”) } for (i in 100 downTo 1 step 2) { //downTo는 역방향 수열을 만든다 //step은 증가 값의 절댓값이 2로 바뀐다. print(“even count down : ${i}”) } val list = arrayListOf(“사이다”, “콜라”, “주스”) println(“[메뉴]”) for((index, element) in list.withIndex()) { //인덱스와 함께 컬렉션을 이터레이션 한다. println(“${index} : ${element}”) }
in으로 컬렉션이나 범위 검사
fun recognize(c: Char) = when (c) { in ‘0’..’9′ -> “digit” in ‘a’..’z’, in ‘A’..’Z’ -> “letter” else -> “ERROR” } print(“OrangeJuice” in “Milk”..”Coffee”) //”OrangeJuice” <= "Milk" && "OrangeJuice" <= "Coffee"와 동일하다 print("OrangeJuice" in setOf("Milk","Coffee")) //이 집합에 OrangeJuice가 들어있는지 확인한다. 예외 처리 if(number !in 0..100) { throw IllegalArgumentException("This is not between 0~100 : ${number}") //throw 또한 식으로, 다른 식에 포함될 수 있다. } val checkNumber = if(number !in 0..100) { number } else { throw IllegalArgumentException("This is not between 0~100 : ${number}") //false 이면 변수가 초기화 되지 않는다. 출력시 null 이 반환된다. } //예외 처리 시 try, catch, finally를 함께 사용하며, 자바와 동일하다. val readNumber(reader: bufferedReader) : Int? { try{ val line = reader.readLine() return Integer.parseInt(line) //try 또한 if, when 과 같이 식이므므로 변수에 대입할 수 있다. 이때 catch 는 return 만 사용하면 그 이상 실행되지 않으나, 해당 대입 이후 기본 값으로 진행을 원할 때는 null 등을 이용하면 된다. } catch (e: NumberFormatException) { return null } finally { reader.close() } } 반응형
[kotlin] 코틀린 인 액션(1) – 코틀린이란?
코틀린이 정한 목표 영역은 상당히 광범위하다. 코틀린은 어느 한문제 영역만을 해결하거나 오늘날 소프트웨어 개발이 처한 어려움 중 일부만을 다루기 위한 언어가 아니다. 대신 코틀린은 개발 과정에서 수행해야 하는 모든 과업에 있어 폭넓게 생산성을 향상시켜 준다. 코틀린은 구체적인 영역의 문제를 해결하거나 특정 프로그래밍 패러다임을 지원하는 여러 라이브러리와 아주 잘 융합된다. 다음은 코틀린 언어의 핵심적인 특징이다.
코틀린 언어 특징
1. 정적 타입 지정 언어
자바와 마찬가지로 코틀린도 정적 타입 지정 언어다. 정적 타입 지정이라는 말은 모든 프로그램 구성 요소의 타입을 컴파일 시점에 알 수 있고 프로그램 안에서 객체의 필드나 메소드를 사용할 때마다 컴파일러가 타입을 검증해준다는 뜻이다. JVM에서는 Groovy나 JRuby가 대표적인 동적 타입 지정 언어다. 동적 타입 지정 언어에서는 타입과 관계없이 모든 값을 변수에 넣을 수 있고, 메소드나 필드 접근에 대한 검증이 실행시점에 일어나며, 그에 따라 코드가 더 짧아지고 데이터 구조를 더 유연하고 생서하고 사용할 수 있다. 하지만 반대로 이름을 잘못 입력하는 등의 실수도 커파일 시 걸러내지 못하고 실행 시점에 오류가 발생한다.
한편 자바와 달리 코틀린에서는 모든 변수의 타입을 프로그래머가 직접 명시할 필요가 없다. 대부분의 경우 코틀린 컴파일러가 문맥으로부터 변수 타입을 자동으로 유추할 수 있기 때문에 프포그래머는 타입 선언을 생략해도 된다. 컴파일러가 문맥을 고려해 변수 타입을 결정하는 이런 기능을 타입 추론(type inference)라고 부른다. 정적 타입 지정의 장점은 다음과 같다.
1) 성능
실행 시점에 어떤 메소드를 호출할지 알아내는 과정이 필요 없으므로 메소드 호출이 더 빠르다.
2) 신뢰성
컴파일러가 프로그램의 정확성(correctness)을 검증하기 때문에 실행시 프로그램이 오류로 중단될 가능성이 더 적어진다.
3) 유지보수성
코드에서 다루는 객체가 어떤 타입에 속하는지 알 수 있기 때문에 처음 보는 코드를 다룰때도 더 쉽다.
4) 도구 지원
정적 타입 지정을 활용하면 더 안전하게 리팩토링할 수 있고, 도구는 더 정확한 코드 완성 기능을 제공할 수 있으며, IDE의 다른 지원 기능도 더 잘 만들수 있다.
코틀린은 타입 추론을 지원하므로 정적 타입 지정 언어에서 프로그래머가 직접 타입을 선언해야 함에 따라 생기는 불편함이 대부분 사라진다. 코틀린의 타입 시스템을 더 자세히 살펴보면 이미 잘알고 있는 내용을 많이 발경할 수 있다. 클래스, 인터페이스, 제네릭스는 모두 자바와 비슷하게 작동한다. 하지만 몇가지 새로운 점이 있다.
그 중 가장 중요한 특성은 코틀리이 널이 될수 있는 타입(nullable type)을 지원한다는 점이다. 널이 될 수 있는 타입을 지원함에 따라 컴파일 시점에 null pointer exception이 발생할 수 있는지 여부를 검사할 수 있어서 좀더 프로그램의 신뢰성을 높일 수 있다. 코틀린 타입 시스템은 또한 함수 타입(function type)에 대한 지원을 들 수 있다.
코틀린은 함수형 스타일로 프로그램을 짤 수 있게 지원하지만 함수형 프로그래밍 스타일을 강제하지는 않는다. 명령형 방식이 더 적합한 경우라면 함수형 프로그래밍으로 번거롭게 코드를 작성할 필요없이 직접 변경 가능한 데이터와 부수 효과를 활용하는 함수를 사용해도 된다. 코틀린으로 코드를 작성할때는 객체지향과 함수형 접근 방법을 함께 조합해서 문제에 가장 적합한 도구를 사용하면 된다.
1. 코틀린 서버 프로그래밍
서버 프로그래밍은 상당히 광범위한 개념이다. 다음과 같은 응용 분야를 포함하는 여러분야가 서버 프로그래밍에 포함된다.
– 브라우저에 HTML 페이지를 돌려주는 웹 애플리케이션
– 모바일 애플리케이션에게 HTTP를 통해 JSON API를 제공하는 백엔드 애플리케이션
– RPC(원격 프로시저 호출) 프로토콜을 통해 서로 통신하는 작은 서비스들로 이뤄진 마이크로서비스
개발자들은 이런 애플리케이션을 수년간 자바로 개발해 오면서 이런 종류의 애플리케이션 개발에 도움을 줄 수 있는 기술과 프레임워크를 만들어왔다. 새로운 기술이나 프레임워크는 언제나 기존 프레임워크나 기술을 확장하고 개선하거나 대치하며, 이미 여러 해 동안 쓰여온 기존 시스템과 새로운 코드를 통합해야만 한다.
이런 환경에서 자바 코드와 매끄럽게 상호운용할 수 있다는 점이 코틀린의 큰 장점이다. 코틀린은 새로운 컴포넌트를 작성하거나 기존 서비스 코드를 코틀린으로 이식해야하는 경우에 모두 잘 들어맞는다. 자바 클래스를 코틀린으로 확장해도 아무 문제가 없으며, 코틀린 클래스 안의 메소드나 필드에 특정 자바 어노테이션을 붙여야 하는 경우에도 아무 문제가 없다. 그러면서도 시스템 코드는 더 간결해지고 더 신뢰성이 높아지며, 더 유지보수하기 쉬워질 것이다.
2. 코틀린 안드로이드 프로그래밍
전형적인 모바일 애플리케이션은 전형적인 엔터프라이즈 애플리케이션과 아주 많이 다르다. 모바일 애플리케이션은 엔터프라이즈 애플리케이션보다 더 작고 기존 코드 기반과 새 코드를 통합할 필요도 더 적다. 또 모바일 애플리케이션은 보통 더 다양한 디바이스에 대해 서비스의 신뢰성을 보장하면서 더 빠르게 개발해 배포할 필요가 있다. 코틀린 언어의 특성과 안드로이드 프레임워크의 특별한 컴파일러 플러그인 지원을 조합하면 안드로이드 애플리케이션 개발의 생산성을 더 높일 수 있다.
코틀린을 사용하면 얻을 수 있는 이익으로는 애플리케이션의 신뢰성이 더 높아진다는 점이다. 안드로이드 앱의 ‘Process Has Stopped’ 대화상자는 애플리케이션에서 처리되지 않는 예외가 발생한 경우에 표시된다. 코틀린 타입 시스템은 null 값을 정확히 추적하며 널 포인터로 인해 생기는 문제를 줄여준다. 자바에서 Null Pointer Exception을 일으키는 유형의 코드는 대부분 코틀린에서는 컴파일도 되지 안흔ㄴ다. 따라서 개발 중인 애플리케이션이 릴리즈되기 전에 널 포인터 관련 오류를 수정할 수 있다.
코틀린을 사용하더라도 성능 측면에서 아무 손해가 없다. 코틀린 컴파일러가 생성한 바이트코드는 일반적인 자바 코드와 똑같이 효율적으로 실행된다. 코틀린의 런타임 시스템은 상당히 작기 때문에 컴파일 후 패키징한 애플리케이션 크기도 자바 애플리케이션에 비해 그리 많이 늘어나지 않는다. 대부분의 코틀린 표준 라이브러리 함수는 인자로 받은 람다 함수를 인라이닝한다. 따라서 람다를 사용해도 새로운 객체가 만들어지지 않으므로 객체 증가로 인해 GC가 늘어나서 프로그램이 자주 멈추는 일도 없다.
코틀린의 철학
코틀린이 자바와의 상호운용성에 초점을 맞춘 실용적이고 간결하며 안전한 언어라고 설명하는 경우가 자주 있다. 그렇다면 실용성, 간결성, 안전성, 상호운용성은 각각 어떤 의미인지 살펴본다.
1. 실용성
코틀린은 실제 문제를 해결하기 위해 만들어진 실용적인 언어다. 코틀린 설계는 대규모 시스템을 개발해온 다년간의 IT업계 경험을 바탕으로 이루어졌으며, 수많은 소프트웨어 개발자들의 사용에 잘 들어맞을 수 있게 주의깊게 언어 특성을 선택했다. 더 나아가 젯브레인스나 코틀린 커뮤니티 내부의 개발자들이 다년간 코틀린 초기 버전을 사용하면서 전달한 피드백이 현재 발표된 최종 코틀린 버전에 반영돼 있다. 그런 이유로 실제 프로젝트에서 문제를 해결할때 코틀린이 도움이 될 여지가 많다.
코틀린은 다른 프로그래밍 언어가 채택한 이미 성공적으로 검증된 해법과 기능에 의존한다. 이로 인해 언어의 복잡도가 줄어들고 이미 알고 있는 기존 개념을 통해 코틀린을 더 쉽게 배울 수 있다. 코틀린은 어느 특정 프로그래밍 스타일이나 패러다임을 사용할 것을 강제로 요구하지 않는다. 코틀린을 처음 배우는 사람은 자바에서 사용해 온 익숙한 프로그래밍 스타일이나 기법을 활용할 수 있다. 나중에 코틀린의 더 강력한 특성을 발견하고 그런 특성을 자신의 코드에 적용하는 방법을 배우고 나면 그 특성을 잘 활용해서 간결하게 코드를 작성할 수 있다.
코틀린의 경우 인텔리J 아이디어의 개발과 컴파일러의 개발이 맞물려 이뤄져 왔다. 그리고 코틀린 언어의 특성은 항상 도구의 활용을 염두에 두고 설계돼 왔다. 코틀린의 여러 특성을 배울때도 IDE의 코틀린 언어 지원이 중요한 역할을 한다. 흔히 쓰이지만 더 간결한 구조로 바꿀 수 있는 대부분의 코드 패턴을 도구가 자동으로 감지해서 수정하라고 제안한다. 이런 자동 수정 안내를 살펴보면서 코틀린 언어의 특성을 잘 이해하면 자신의 코드에 그런 특성을 적용하는 방법을 배울 수 있다.
2. 간결성
개발자가 코드를 새로 작성하는 시간보다 기존 코드를 읽는 시간이 더 길다. 코드가 더 간단하고 간결할수록 내용을 파악하기가 더 쉽다. 물론 설계가 좋고 각 부분의 역할을 잘 표현해주는 적절한 이름이 붙어있다면 내용을 파악할 때 큰 도움이 된다. 그러나 어떤 언어를 사용해 코드를 작성했고 그 언어가 얼마나 간결한 언어인지도 중요하다. 어떤 언어가 간결하다는 말은 그 언어로 작성된 코드를 읽을때 의도를 쉽게 파악할 수 있는 구문 구조를 제공하고, 그 의도를 달성하는 방법을 이해할 때 방해가 될 수 있는 부가적인 준비 코드가 적다는 뜻이다.
코틀린은 프로그래머가 작성하는 코드에서 의미가 없는 부분을 줄이고, 언어가 요구하는 구조를 만족시키기 위해 의미없이 프로그램에 꼭 넣어야 하는 부수적인 요소를 줄이기 위해 많은 노력을 기울였다. getter, setter, 생성자 파라미터를 필드에 대입하기 위한 로직 등 자바에 존재하는 여러가지 번거로운 준비 코드를 코틀린은 무시적으로 제공하기때문에 그런 준비 코드로 인해 지저분해지는 일이 없다.
코드가 불필요하게 길어지는 또다른 이유는 컬렉션에서 원소를 찾는 것과 같은 일반적인 작업을 수행하기 위해 명시적으로 작성해야만 하는 코드의 양이 상당하기 때문이다. 코틀린은 기능이 다양한 표준 라이브러리를 제공하기때문에 반복되거나 길어질 수 있는 코드를 라이브러리 함수 호출로 대치할 수 있다. 람다를 지원하기 때문에 작은 코드 블록을 라이브러리 함수에 쉽게 전달할수도 있다. 따라서 일반적인 기능을 라이브러리 안에 캡슐화하고 작업에 따라 달라져야 하는 개별적인 내용을 사용자가 작성한 코드 안에 남겨둘 수 있다.
코드가 더 간결하면 쓰는데 시간이 덜 걸린다. 더 중요한것은 읽는데도 시간이 덜 걸린다는 점이다. 간결성은 생산성을 향상시켜주고 개발을 더 빠르게 진행할 수 있게 해준다.
3. 안전성
일반적으로 프로그래밍 언어가 안전하다는 말은 프로그램에서 발생할 수 있는 오류 중에서 일부 유형의 오류를 프로그램 설계가 원천적으로 방지해준다는 뜻이다. 컴파일러에게 프로그램이 어떻게 작동해야 하는지에 대한 정보를 더 자세히 제공해야만 컴파일러가 프로그램 코드와 프로그램의 작동 의도에 대한 정보가 일치하는지를 검증할 수 있다. 따라서 더 큰 안전성을 얻기 위해서는 프로그램에 더 많은 정보를 덧붙여야 하므로 생산성이 하락하는 것을 감수해야하며 안전성과 생산성 사이에는 트레이드오프 관계가 성립한다.
코틀린을 JVM에서 실행한다는 사실은 이미 상당한 안전성을 보장할 수 있다는 뜻이다. 예를 들어 JVM을 사용하면 메모리 안전성을 보장하고, 버퍼 overflow를 방지하며, 동적으로 할당한 메모리를 잘못 사용함으로 인해 발생할 수 있는 다양한 문제를 예방할 수 있다. JVM에서 실행되는 정적 타입 지정 언어로서 코틀린은 애플리케이션의 타입 안전성을 보장한다. 하지만 자바보다 더 적은 비용으로 타입 안전성을 사용할 수 있다. 대부분의 경우 코틀린 컴파일러가 타입을 자동으로 추론해주기 때문에 직접 타입 정보를 지정할 필요가 없다.
코틀린은 실행 시점에 오류를 발생시키는 대신 컴파일 시점 검사를 통해 오류를 더 많이 방지해준다. 가장 중요한 내용으로 코틀린은 프로그램의 NullPointerException을 없애기 위해 노력한다. 코틀린의 타입 시스템은 null이 될수 없는 값을 추적하며, 실행 시점에 NullPointerException이 발생할 수 있는 연산을 사용하는 코드를 금지한다.
추가로 코틀린은 널이 될 수 있는 값을 다룰 수 있는 편리한 방법을 다양하게 제공한다. 이런 기능은 애플리케이션이 NullPointerException으로 인해 갑자기 중단되는 경우를 많이 줄여준다. 코틀린이 방지해주는 다른 예외로는 ClassCastException이 있다. 어떤 객체를 다른 타입으로 캐스트(cast)하기 전에 타입을 미리 검사하지 않으면 ClassCastException이 발생할 수도 있다. 코틀린에서는 타입 검사와 캐스트가 한 연산자에 의해 이뤄진다. 어떤 객체의 타입을 검사했고 그 객체가 그 타입에 속한다면 해당 타입의 메소드나 필드 등의 멤버를 별도의 캐스트 없이 사용할 수 있다. 따라서 타입 검사를 생략할 이유가 없고, 검사를 생략하지 않으면 검사를 생략해서 생기는 오류가 발생할 일도 없다.
if (value is String) // 타입을 검사한다 println(value.toUpperCase()) // 해당 타입의 메소드를 사용한다.
4. 상호운용성
코틀린은 자바 라이브러리가 어떤 API를 제공하던 간에 코틀린에서 그 API를 활용할 수 있다. 자바 메소드를 호출하거나 자바 클래스를 상속하거나 인터페이슬 구현하거나 자바 어노테이션을 코틀린 코드에 적용하는 등의 일이 모두 가능하다. 다른 일부 JVM 언어와 달리 코틀린은 상호운용성 측면에서 훨씬 더 많은 것을 제공한다. 코틀린의 클래스나 메소드를 일반적인 자바 클래스나 메소드와 똑같이 사용할 수 있다. 이에 따라 자바와 코틀린 코드를 프로젝트에서 원하는대로 섞어 쓸 수 있는 궁극적인 유연성을 발휘할 수 있다. 기존 자바 프로젝트에 코틀린을 도입하는 경우 자바를 코틀린으로 변환하는 도구를 코드베이스 안에 있는 자바 클래스에 대해 실행해서 그 클래스를 코틀린 클래스로 변환할 수 있다. 이렇게 변경한 클래스가 프로젝트 안에서 어떤 역할을 하는지와는 관계없이 코틀린으로 바꾼 클래스가 어떤 것이든 프로젝트의 나머지 부분을 전혀 수정하지 않고도 컴파일 및 실행이 가능하다.
상호운용성 측면에서 코틀린이 집중하는 다른 방향으로는 기존 자바 라이브러리를 가능하면 최대한 활용한다는 점을 들 수 있다. 코틀린은 자체 컬렉션 라이브러리를 제공하지 않는다. 코틀린은 자바 표준 라이브러리 클래스에 의존한다. 다만 코틀린에서 컬렉션을 더 쉽게 활용할 수 있게 몇가지 기능을 더할 뿐이다.
코틀린이 제공하는 도구도 다중 언어 프로젝트를 완전히 지원한다. 코틀린은 자바와 코틀린 소스 파일이 임의로 섞여 있어도 제대로 프로그램을 컴파일할 수 있다. 각 소스 파일 사이의 의존관계가 어떤 식으로 이뤄졌든 관계없이 컴파일할 수 있다. IDE 기능도 언어와 관계없이 제대로 동작하다.
– 자바와 코틀린 소스 파일을 자유롭게 내비게이션할 수 있다.
– 여러 언어로 이뤄진 프로젝트를 디버깅하고 서로 다른 언어로 작성된 코드를 언어와 관계없이 한 단계씩 실행할 수 있다.
– 자바 메소드를 리팩토링해도 그 메소드와 관련있는 코틀린 코드까지 제대로 변경된다. 역으로 코틀린 메소드를 리팩토링해도 자바 코드까지 모두 자동으로 변경된다.
코틀린 도구 사용
코틀린 소스코드를 저장할 때는 보통 .kt라는 확장자를 파일에 붙인다. 코틀린 컴파일러는 자바컴파일러가 자바 소스코드를 컴파일할 때와 마찬가지로 코틀린 소스코드를 분석해서 .class 파일을 만들어낸다. 만들어진 .class 파일은 개발 중인 애플리케이션의 유형에 맞는 표준 패키징 과정을 거쳐 실행될 수 있다. 가장 간단한 방식은 커맨드라인에서 kotlinc 명령을 통해 코틀린 코드를 컴파일한 다음 java 명령으로 그 코드를 실행하는 것이다.
kotlinc <소스파일 또는 디렉토리> -include-runtime -d
java -jar 다음은 코틀린 빌드 과정을 간단히 보여준다.
코틀린 컴파일러로 컴파일한 코드는 코틀린 런타임 라이브러리(kotlin runtime library)에 의존한다. 런타임 라이브러리에는 코틀린 자체 표준 라이브러리 클래스와 코틀린에서 자바 API의 기능을 확장한 내용이 들어있다. 코틀린으로 컴파일한 애플리케이션을 배포할때는 런타임 라이브러리도 함께 배포해야 한다.
실제로 개발을 진행한다면 프로젝트를 컴파일하기 위해 메이븐, 그레이들, 앤트 등의 빌드 시스템을 사용하게 될것이며, 코틀린은 그런 빌드 시스템과 호환된다. 이런 빌드 시스템은 모두 코틀린과 자바가 코드베이스에 함께 들어있는 혼합 언어 프로젝트를 지원할 수 있다. 메이븐과 그레이들은 애플리케이션을 패키지할 때 알아서 코틀린 런타임을 포함시켜준다.
자바-코틀린 변환기
새로운 언어를 배워 써먹을 만큼 숙련도를 높이려면 많이 노력해야 한다. 이 도구는 자동으로 자바를 코틀린으로 변환한다. 코틀린을 처음 배웠는데 정확한 코틀린 문법이 기억나지 않는 경우 이 변환기를 유용하게 써먹을 수 있다. 작성하고픈 코드를 자바로 작성해 복사한 후 코틀린 파일에 그 코드를 붙여 넣으면 변환기가 자동으로 같은 뜻의 코틀린 코드를 제안한다. 물론 변환기가 항상 가장 코틀린다운 코드를 제안해주지는 못하지만 잘 작동하는 코틀린 코드를 알려주기 때문에 원하는 바를 코틀린으로 달성할 수 있다.
기존 자바 프로젝트에 코틀린을 도입하고 싶을때 변환기를 사용하면 쓸모가 있다. 새 클래스를 작성할 필요가 있다며 ㄴ처음부터 코틸린으로 그 클래스를 만들면 된다. 기존 클래스를 상당 부분 변경해야 한다면 자바 대신 코틀린을 사용하고 싶을텐데 그런경우 변환기를 사용하면 도움이된다.
intellij에서 변화기를 사용하기는 쉽다. 자바 코드 조각을 변환하고 싶을때는 자바 코드 조각을 복사해 코틀린 파일에 붙여넣는다. 자바 파일 하나를 통째로 코틀린으로 변환하고 싶으면 메뉴에서 [Code] – [Convert Java File to Kotlin File] 을 선택하면 된다.
코틀린 기본 요소: 함수와 변수
모든 프로그램을 구성하는 기본 단위인 함수와 변수를 살펴본다. 코틀린에서 타입 선언을 생략해도 된다는 사실을 보고, 코틀린이 어떻게 변경 가능한 데이터보다 변경할 수 없는 불변 데이터 사용을 장려하는지 배운다.
– 함수를 최상위 수준에서 정의할 수 있다. 자바와 달리 클래스 안에 함수를 넣어야할 필요가 없다.
– 배열도 일반적인 클래스와 마찬가지다. 코틀린에는 자바와 달리 배열 처리를 위한 문법이 따로 존재하지 않는다.
– System.out.println 대신에 println이라고 쓴다. 코틀린 표준 라이브러리는 여러가지 표준 자바 라이브러리 함수를 간결하게 사용할 수 있게 감싼 wrapper를 제공한다. println도 그런 함수 중 하나다.
– 최신 프로그래밍 언어 경향과 마찬가지로 줄 끝에 세미콜론을 붙이지 않아도 좋다.
1. 함수
문(statement)와 식(expression)의 구분
코틀린에서 if는 식이지 문이 아니다. expression은 값을 만들어내며 다른 expression의 하위 요소로 계산에 참여할 수 있는 statement는 자신을 둘러싸고 있는 가장 안쪽 블록의 최상위 요소로 존재하며 아무런 값을 만들어내지 않는다는 차이가 있다. 자바에서는 모든 제어구조가 statement인 반면 코틀린에서는 루프를 제외한 대부분의 제어 구조가 expression이다. 제어 구조를 다른 expression으로 엮어낼 수 있으면 여러 일반적인 패턴을 아주 간결하게 표현할 수 있다.
반면 대입문은 자바에서는 expression이었으나 코틀린에서는 statement가 되었다. 그로인해 자바와 달리 대입식과 비교식을 잘못 바꿔 써서 버그가 생기는 경우가 있다.
식이 본문인 함수
fun max(a: Int, b: Int): Int { return if (a > b) a else b }
위와 같이 본문이 중괄호로 둘러싸인 함수를 블록이 본문인 함수라 부르고,
fun max(a: Int, b: Int): Int = if (a > b) a else b
등호와 식으로 이뤄진 함수를 식이 본문인 함수라고 부른다.
인텔리J IDEA는 이 두 방식의 함수를 서로 변환하는 메뉴가 있다. 각각 ‘Convert to expression body’와 ‘Convert to block body’이다.
코틀린에서는 식이 본문인 함수가 자주 쓰인다. 그런 함수의 본문 식에는 단순한 산술식이나 함수 호출 식뿐만 아니라 if, when, try 등의 더 복잡한 식도 자주 쓰인다. 식이 본문인 함수는 반환 타입이 생략 가능하다. 블록이 본문인 함수가 값을 반환한다면 반드시 반환 타입을 지정하고 return문을 사용해 반환 값을 명시해야 한다.
2. 변수
코틀린에서는 타입 지정을 생략하는 경우가 흔하다. 타입으로 변수 선언을 시작하면 타입을 생략할 경우 식과 변수 선언을 구별하 ㄹ수 없다. 그런 이유로 코틀린에서는 키워드로 변수 선언을 시작하는 대신 변수 이름 뒤에 타입을 명시하거나 생략하게 허용한다.
val question = “삶, 우주, 그리고 모든 것에 대한 궁극적인 질문” val answer = 42 val answer: Int = 42
식이 본문인 함수에서와 마찬가지로 타입을 지정하지 않으면 컴파일러가 초기화 식을 분석해서 초기화 식의 타입을 변수 타입으로 지정한다. 초기화 식을 사용하지 않고 변수를 선언하려면 변수 타입을 반드시 명시해야 한다. 초기화 식이 없다면 변수에 저장될 값에 대해 아무 정보가 없기 때문에 컴파일러가 타입을 추론할 수가 없다. 따라서 그런 경우 타입을 반드시 지정해야 한다.
1) 변경 가능한 변수와 불가능한 변수
변수 선언 시 사용하는 키워드는 다음과 같은 2가지가 있다.
– val (값을 뜻하는 value에서 따옴) : 변경 불가능한 참조를 저장하는 변수다 val로 선언된 변수는 일단 초기화하고 나면 재대입이 불가능하다. 자바로 말하자면 final 변수에 해당한다.
– var (변수를 뜻하는 variable에서 따옴) : 변경 가능한 참조다. 이런 변수의 값은 바꿀 수 있다. 자바의 일반 변수에 해당한다.
기본적으로 모든 변수를 val 키워드를 사용해 불변 변수로 선언하고, 나중에 꼭 필요할때에만 var로 변경하라. 변경 불가능한 참조와 변경 불가능한 객체를 부수 효과가 없는 함수와 조합해 사용하면 코드가 함수형 코드에 가까워진다.
val 변수는 블록을 실행할 때 정확히 한번만 초기화돼야 한다. 하지만 어떤 블록이 실행될때 오직 한 초기화 문장만을 실행됨을 컴파일러가 확인할 수 있다면 조건에 따라 val 값을 다른 여러 값으로 초기화할 수도 있다.
val message: String if (canPerformOperation()) { message = “Success” // … 연산을 수행한다. } else { message = “Failed” }
val 참조 객체는 불변일지라도 그 참조가 가리키는 객체의 내부 값은 변경될 수 있다.
val languages = arrayListOf(“Java”) // 불변 참조를 선언한다. languages.add(“Kotlin”) // 참조가 가리키는 객체 내부를 변경한다.
var 키워드를 사용하면 변수의 값을 변경할 수 있지만 변수의 타입은 고정돼 바뀌지 않는다. 예를 들어 다음 코드는 컴파일할 수 없다.
var answer = 42 answer = “no answer” // “Error:type mismatch” 컴파일 오류 발생
문자열 리터럴(string literal)에서 컴파일 오류가 발생한다. 이유는 그 타입(String)이 컴파일러가 기대하는 타입(Int)와 다르기 때문이다. 컴파일러는 변수 선언 시점의 초기화 식으로부터 변수의 타입을 추론하며, 변수 선언 이후 변수 재대입이 이뤄질 때는 이미 추론한 변수의 타입을 염두에 두고 대입문의 타입을 검사한다.
어떤 타입의 변수에 다른 타입의 값을 지정하고 싶다면 변환 함수를 써서 값을 변수의 타입으로 반환하거나, 값을 변수에 대입할 수 있는 타입으로 강제 형 변환해야한다.
2) 문자열 형식 지정: 문자열 템플릿
코틀린의 문자열 템플릿은 자바 문자열 접합 연산을 사용한 식과 마찬가지로 효율적이다. 컴파일된 코드는 StringBuilder를 사용하고 문자열 상수와 변수의 값을 append로 문자열 빌더 뒤에 추가한다. 자바에서 + 연산으로 문자열과 변수를 붙여도 컴파일러는 StringBuilder를 사용하는 바이트코드를 생성해준다.
코틀린에서는 자바와 마찬가지로 한글(한글 뿐 아니라 ‘글자(letter)’로 분류할 수 있는 모든 유니코드 문자)을 식별자에 사용할 수 있으므로 변수 이름에 한글이 들어갈 수 있다. 그런 유니코드 변수 이름으로 인해 문자열 템플릿을 볼때 오해가 생길 수 있다. 문자열 템플릿 안에 $로 변수를 지정할 때 변수명 바로 뒤에 한글을 붙여서 사용하면 코틀린 컴파일러는 영문자와 한글을 한꺼번에 식별자로 인식해서 unresolved reference 오류를 발생시킨다.
이 문제를 해결하는 방법은 ‘${name}님 반가와요!’처럼 변수 이름을 {}로 감싸는 것이다. 문자열 템플릿 안에서 변수 이름만 사용하는 경우라도 ${name}처럼 중괄호로 변수명을 감싸는 습관을 들이면 더 좋다. 필요할 때 정규식 등을 통해 검색하거나 일괄 변환할 때도 중괄호를 쓴 경우 처리가 더 쉽고, 코드를 사람이 읽을 때도 문자열 템플릿 안에서 변수가 쓰인 부분을 더 쉽게 식별할 수 있다.
문자열 템플릿 안에 사용할 수 있는 대상은 간단한 변수 이름만으로 한정되지 않는다. 복잡한 식도 중괄호({})로 둘러싸서 문자열 템플릿 안에 넣을 수 있다.
fun main(args: Array
) { if (args.size > 0) { println(“Hello, ${args[0]}!”) // args 배열의 첫번째 원소를 넣기위해 ${} 구문을 사용. } } 중괄호로 둘러싼 식 안에서 큰 따옴표를 사용할 수도 있다.
fun main(args: Array
) { println(“Hello, ${if (args.size > 0) args[0] else “someone”}!”) } 심지어 중괄호로 둘러싼 식 안에서 문자열 템플릿을 사용해도 된다.
“${if(s.length > 2) “too short” else “normal string ${s}”}”
클래스와 프로퍼티
코틀린을 활용하면 더 적은 양의 코드로 클래스와 관련있는 대부분의 작업을 수행할 수 있다. 코드가 없이 데이터만 저장하는 클래스를 값 객체(value object)라 부르며, 다양한 언어가 값 객체를 간결하게 기술할 수 있는 구문을 제공한다. 자바를 코틀린으로 변환하게 되면 public 가시성 변경자가 사라지게 되는데, 코틀린의 기본 가시성은 public이므로 이런 경우 변경자를 생략해도 된다.
1. 프로퍼티
클래스라는 개념의 목적은 데이터를 캡슐화하고 캡슐화한 데이터를 다루는 코드를 한 주체 아래 가두는 것이다. 자바에서는 데이털르 필드에 저장하며, 멤버 필드의 가시성은 보통 비공개다. 클래스는 자신을 사용하는 칼리언트가 그 데이터에 접근하는 통로로 쓸수 있는 접근자 메소드(accessor method)를 제공한다. 보통은 필드를 읽기 위한 게터(getter)를 제공하고 필드를 변경하게 허용해야 할 경우 세터(setter)를 추가 제공할 수 있다. 세터는 자신이 받은 값을 검증하거나 필드 변경을 다른 곳에 통지하는 등의 로직을 더 가질 수 있다.
자바에서는 필드와 접근자를 한데 묶어 프로퍼티라고 부르며, 프로퍼티라는 개념을 활용하는 프레임워크가 많다. 코틀린은 프로퍼티를 언어 기본 기능으로 제공하며, 코틀린 프로퍼티는 자바의 필드와 접근자 메소드를 완전히 대신한다. 클래스에서 프로퍼티를 선언할 때는 앞에서 살펴본 변수를 선언하는 방법과 마찬가지로 val이나 var를 사용한다. val로 선언한 프로퍼티는 읽기 전용이며, var로 선언한 프로퍼티는 변경 가능하다.
class Person { val name: String, // 읽기 전용 프로퍼티로, 코틀린은 (비공개) 필드와 필드를 읽는 단순한 (공개) 게터를 만들어낸다. var isMarried: Boolean // 쓸 수 있는 프로퍼티로, 코틀린은 (비공개) 필드, (공개) 게터, (공개) 세터를 만들어낸다. }
기본적으로 코틀린에서 프로퍼티를 선언하는 방식은 프로퍼티와 관련있는 접근자를 선언하는 것이다. 읽기 전용 프로퍼티의 경우 게터만 선언하며 변경할 수 있는 프로퍼티의 경우 게터와 세터를 모두 선언한다. 코틀린은 값을 저장하기 위한 비공개 필드와 그 필드에 값을 저장하기 위한 세터, 필드의 값을 읽기 위한 게터로 이뤄진 간단한 디폴트 접근자 구현을 제공한다.
게터와 세터의 이름을 정하는 규칙에는 예외가 있다. 이름이 is로 시작하는 프로퍼티의 게터에는 get이 붙지 않고 원래 이름을 그대로 사용하며, 세터에는 is를 set으로 바꾼 이름을 사용한다. 따라서 자바에서는 isMerried 프로퍼티의 게터를 호출하려면 isMerried()를 사용해야 한다.
자바에서 선언한 클래스에 대해 코틀린 문법을 사용해도 된다. 코틀린에서는 자바 클래스의 getter를 val 프로퍼티처럼 사용할 수 있고, 게터/세터 쌍이 있는 경우에는 var 프로퍼티처럼 사용할 수 있다. 예를 들어 setName과 getName이라는 접근자를 제공하는 자바 클래스를 코틀린에서 사용할 때는 name이라는 프로퍼티를 사용할 수 있다. 자바 클래스가 isMarried와 setMarried 메소드를 제공한다면 그에 상응하는 코틀린 프로퍼티의 이름은 isMarried다.
대부분의 프로퍼티에는 그 프로퍼티의 값을 저장하기 위한 필드가 있다. 이를 프로퍼티를 뒷받침하는 필드(backing field)라고 부른다. 하지만 원한다면 프로퍼티 값을 그때그때 계산할 수도 있다. 커스텀 게터를 작성하면 그런 프로퍼티를 만들수 있다.
코틀린 소스코드 구조: 디렉토리와 패키지
자바의 경우 모든 클래스를 패키지 단위로 관리한다. 코틀린에도 자바와 비슷한 개념의 패키지가 있다. 모든 코틀린 파일의 맨 앞에 package 문을 넣을 수 있다. 그러면 그 파일 안에 있는 모든 선언(클래스, 함수, 프로퍼티 등)이 해당 패키지에 들어간다. 같은 패키지에 속해 있다면 다른 파일에서 정의한 선언일지라도 직접 사용할 수 있다. 반면 다른 패키지에서 정의한 선언을 사요하려면 임포트를 통해 선언을 불러와야 한다. 자바와 마찬가지로 임포트문은 파일의 맨 앞에 와야하며 import 키워드를 사용한다.
코틀린에서는 클래스 임포트와 함수 임포트에 차이가 없으며, 모든 선언을 Import 키워드로 가져올 수 있다. 최상위 함수는 그 이름을 써서 임포트할 수 있다.
package geometry.example import geometry.shapes.createRandomRectangle // 이름으로 함수 임포트하기 fun main(args: Array
) { println(createRandomRectangle().isSquare) } 패키지 이름 뒤에 .*를 추가하면 패키지 안의 모든 선언을 임포트할 수 있다. 이런 스타 임포트(star import)를 사용하면 패키지 안에 있는 모든 클래스뿐 아니라 최상위에 정의된 함수나 프로퍼티까지 모두 불러오게 된다.
자바에서는 패키지의 구조와 일치하는 디렉토리 계층 구조를 만들고 클래스의 소스코드를 그 클래스가 속한 패키지와 같은 디렉토리에 위치시켜야 한다. (자바에서는 디렉토리 구조가 패키지 구조를 그대로 따라야 한다)
코틀린에서는 여러 클래스를 한 파일에 넣을 수 있고, 파일의 이름도 마음대로 정할 수 있다. 코틀린에서는 디스크상의 어느 디렉토리에 소스 코드 파일을 위치시키든 관계없다. 따라서 원하시는대로 소스코드를 구성할 수 있다. 예를 들어 geometry.shapes라는 패키지가 있다면 그 패키지의 모든 내용을 shapes.kt라는 파일에 넣고, 하위 패키지에 해당하는 별도의 디렉토리를 만들지 않고 geometry라는 폴더 안에 shapes.kt를 넣어도 된다.
하지만 대부분의 경우 자바와 같이 패키지별로 디겍토리를 구성하는 편이 낫다. 특히 자바와 코틀린을 함께 사용하는 프로젝트에서는 자바의 방식을 따르는게 중요하다. 자바의 방식을 따르지 않으면 자바 클래스를 코틀린 클래스로 마이그레이션할 때 문제가 생길 수도 있다. 하지만 여러 클래스를 한 파일에 넣는 것을 주저해서는 안된다. 특히 각 클래스를 정의하는 소스코드 크기가 아주 작은 경우 더욱 그렇다.
선택 표현과 처리: enum과 when
when은 자바의 switch를 대치하되 훨씬 더 강력하다. when에 대해 설명하는 과정에서 코틀린에서 enum을 선언하는 방법과 smart cast에 대해서도 살펴본다.
1. enum 클래스 정의
enum은 자바 선언보다 코틀린 선언에 더 많은 키워드를 써야 하는 흔치 않은 예다. 코틀린에서는 enum class를 사용하지만 자바에서는 enum을 사용한다. 코틀린에서 enum은 soft keyword라 부르는 존재다. enum은 class 앞에 있을때는 특별한 의미를 지니지만 다른 곳에서는 이름에 사용할 수 있다. 반면 class는 키워드다. 따라서 class라는 이름을 사용할 수 없으므로 클래스를 표현하는 변수 등을 정의할 때는 clazz나 aClass와 같은 이름을 사용해야 한다.
enum class Color { RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET }
자바와 마찬가지로 enum은 단순히 값만 열거하는 존재가 아니다. enum 클래스 안에도 프로퍼티나 메소드를 정의할 수 있다. 다음은 프로퍼티와 메소드를 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 // enum 클래스 안에서 메소드를 정의한다 }
2. when으로 enum 클래스 다루기
자바 switch문에 해당하는 코틀린 구성요소는 when이다. if와 마찬가지로 when도 값을 만들어내는 식이다. 따라서 식이 본문인 함수에 when을 바로 사용할 수 있다. 다음은 식을 본문으로 하는 함수의 예제이기도 하다.
fun getMnemoic(color: Color) = // 함수의 반환 값으로 when 식을 직접 사용한다. when (color) { // 색이 특정 enum 상수와 같을때 그 상수에 대응하는 문자열을 돌려준다. Color.RED -> “Richard” Color.ORANGE -> “Of” Color.YELLOW -> “York” Color.GREEN -> “Gave” Color.BLUE -> “Battle” Color.INDIGO -> “In” Color.VIOLET -> “Vain” }
앞의 코드는 color로 전달된 값과 같은 분기를 찾는다. 자바와 달리 각 분기의 끝에 break를 넣지 않아도 된다. 성공적으로 매치되는 분기를 찾으면 switch는 그 분기를 실행한다. 한 분기 안에서 여러 값을 매치 패턴으로 사용할 수도 있다. 그럴 경우 값 사이를 콤마(,)로 분리한다.
fun getWarmth(color: Color) = when(color) { Color.RED, Color.ORANGE, Color.YELLOW -> “warm” Color.GREEN -> “neutral” Color.BLUE, Color.INDIGO, Color.VIOLET -> “cold” }
3. when과 임의의 객체를 함께 사용
코틀린에서 when은 자바의 switch 보다 훨씬 더 강력하다. 분기 조건에 상수(enum 상수나 숫자 리터럴)만을 사용할 수 있는 자바 switch와 달리 코틀린 when의 분기 조건은 임의의 객체를 허용한다. 두 색을 혼합했을 때 미리 정해진 팔레트에 들어있는 색이 될 수 있는지 알려주는 함수를 작성해본다.
fun mix(c1: Color, c2: Color) = /* * when 식의 인자로 아무 객체나 사용할 수 있다. * when은 이렇게 인자로 받은 객체가 각 분기 조건에 있는 객체와 같은지 테스트한다. */ when (setOf(c1, c2)) { setOf(RED, YELLOW) -> ORANGE // 두 색을 조합해서 다른 색을 만들 수 있는 경우를 열겨. setOf(YELLOW, BLUE) -> GREEN setOf(BLUE, VIOLET) -> INDIGO else -> throw Exception(“Dirty color”) // 매치되는 분기 조건이 없으면 이 문장을 실행 }
c1과 c2가 RED와 YELLOW라면 그 둘을 혼합한 결과는 ORANGE다. 이를 구현하기 위해 집합 비교를 사용한다. set은 원소가 모여 있는 컬렉션으로, 각 원소의 순서는 중요하지 않다. 모든 분기식에서 만족하는 조건을 찾을수 없다면 else 분기의 문장을 계산한다.
when의 분기 조건 부분에 식을 넣을수 있기 때문에 많은 경우 코드를 더 간결하고 아름답게 작성할 수 있다.
4. 인자없는 when 사용
하지만 위 예제는 약간 비효율적이다. 이 함수는 호출될때마다 함수 인자로 주어진 두 색이 when의 분기조건에 있는 다른 두색과 같은지 비교하기 위해 여러 Set 인스턴스를 생성한다. 보통은 이런 비효율성이 크게 문제가 되지 않는다. 하지만 이 함수가 아주 자주 호출된다면 불필요한 가비지 객체가 늘어나는것을 방지하기 위해 함수를 고쳐 쓰는 편이 낫다. 인자가 없는 when 식을 사용하면 불필요한 객체 생성을 막을 수 있다. 코드는 약간 읽기 어려워지지만 성능을 더 향상시키기 위해 그정도 비용을 감수해야하는 경우도 자주 있다.
fun mixOptimized(c1: Color, c2: Color) = when { (c1 == RED && c2 == YELLOW) || (c1 == YELLOW && c2 == RED) -> ORANGE (c1 == YELLOW && c2 == BLUE) || (c1 == BLUE && c2 == YELLOW) -> GREEN (c1 == BLUE && c2 == VIOLET) || (c1 == VIOLET && c2 == BLUE) -> INDIGO else -> throw Exception(“Dirty color”) }
when에 아무 인자도 없으려면 각 분기의 조건이 불리언 결과를 계산하는 식이여야 한다. mixOptimized는 추가 객체를 만들지 않는다는 장점이 있지만 가독성은 더 떨어진다.
5. 스마트 캐스트: 타입 검사와 타입 캐스트를 조합
(1 + 2) + 4 와 같은 간단한 산술식을 계산하는 함수를 만들어본다. 함수가 받을 산술식에서는 오직 두 수를 더하는 연산만 가능하다. 다른 연산(뺄셈, 곱셈, 나눗셈)도 비슷한 방식으로 구현할 수 있다.
우선 식을 인코딩하는 방법을 생각해야 한다. 식을 트리 구조로 저장하자. 노드는 합계(Sum)나 수(Num) 중 하나다. Num은 항상 말단(leaf 또는 terminal) 노드지만, Sum은 자식이 둘 있는 중간(non-terminal) 노드이다. Sum 노드의 두 자식은 덧셈의 두 인자다. 다음 리스트는 식을 표현하는 간단한 클래스를 보여준다. 식을 위한 Expr 인터페이스가 있고, Sum과 Num 클래스는 그 Expr 인터페이스를 구현한다. Expr은 아무 메소드도 선언하지 않으며, 단지 여러 타입의 식 객체를 아우르는 공통 타입 역할만 수행한다. 클래스가 구현하는 인터페이스를 지정하기 위해서 콜론(:) 뒤에 인터페이스 이름을 사용한다.
interface Expr // value라는 프로퍼티만 존재하는 단순한 클래스로 Expr 인터페이스를 구현한다. class Num(val value: Int) : Expr // Expr 타입의 객체라면 어떤 것이나 Sum 연산의 인자가 될 수 있다. 따라서 Num이나 다른 Sum이 인자로 올 수 있다. class Sum(val left: Expr, val right: Expr) : Expr
Sum은 Expr의 왼쪽과 오른쪽 인자에 대한 참조를 left와 right 프로퍼티로 저장한다. (1 + 2) + 4 라는 식을 저장하면 Sum(Sum(Num(1), Num(2)), Num(4)) 라는 구조의 객체가 생긴다.
Expr 인터페이스에는 두 가지 구현 클래스가 존재한다. 따라서 식을 평가하려면 두가지 경우를 고려해야 한다.
– 어떤 식이 수라면 그 값을 반환한다.
– 어떤 식이 합계라면 좌항과 우항의 값을 계산한 다음에 그 두 값을 합한 값을 반환한다.
fun eval(e: Expr): Int { if (e is Num) { val n = e as Num // 여기서 Num으로 타입을 변환하는데, 이는 불필요한 중복이다. return n.value } if (e is Sum) { return eval(e.right) + eval(e.left) // 변수 e에 대한 스마트 캐스트를 사용한다. } throw IllegalArgumentException(“Unknown expression”) }
코틀린에서는 is를 사용해 변수 타입을 검사한다. is 검사는 자바의 instanceof와 비슷하다. 하지만 자바에서 어떤 변수의 타입을 instanceof로 확인한 다음에 그 타입에 속한 멤버에 접근하기 위해서는 명시적으로 변수 타입을 캐스팅해야 한다. 이런 멤버 접근을 여러번 수행해야 한다면 변수에 따로 캐스팅한 결과를 저장한 후 사용해야 한다. 코틀린에서는 프로그래머 대신 컴파일러가 캐스팅을 해준다. 어떤 변수가 원하는 타입인지 일단 is로 검사하고 나면 굳이 변수를 원하는 타입으로 캐스팅하지 않아도 마치 처음부터 그 변수가 원하는 타입으로 선언된 것처럼 사용할 수 있다. 하지만 실제로는 컴파일러가 캐스팅을 수행해준다. 이를 스마트 캐스트(smart cast)라고 부른다 .
eval 함수에서 e의 타입이 Num인지 검사한 다음 부분에서 컴파일러는 e의 타입을 Num으로 해석하다. 스마트 캐스트는 is로 변수에 든 값의 타입을 검사한 다음에 그 값이 바뀔 수 없는 경우에만 작동한다. 클래스의 프로퍼티에 대해 스마트 캐스트를 사용한다면 그 프로퍼티는 반드시 val이어야 하며 커스텀 접근자를 사용한 것이어도 안된다. val이 아니거나 val이지만 커스텀 접근자를 사용하는 경우에는 해당 프로퍼티에 대한 접근이 항상 같은 값을 내놓는다고 확신할 수 없기 때문이다. 원하는 타입으로 명시적으로 타입 캐스팅하려면 as 키워드를 사용한다.
val n = e as Num
코틀린의 if와 자바의 if는 어떻게 다를까? 코틀린의 if (a > b) a else b는 자바의 a >b ? a : b처럼 작동한다. 코틀린에서는 if가 값을 만들어내기 때문에 자바와 달리 3항 연산자가 따로 없다. 이런 특성을 사용하면 eval 함수에서 return문과 중괄호를 없애고 if 식을 본문으로 사용해 더 간단하게 만들 수 있다.
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 // 이부분에 smart cast가 쓰였다. is Sum -> // 인자 타입을 검사하는 when 분기 eval(e.right) + eval(e.left) // 이부분에 smart cast가 쓰였다. else -> throw IllegalArgumentException(“Unknown expression”) }
when 식을 앞에서 살펴본 값 동등성 검사가 아닌 다른 기능에도 쓸 수 있다. if 예제와 마찬가지로 타입을 검사하고 나면 스마트 캐스트가 이뤄진다. 따라서 Num이나 Sum의 멤버에 접근할 때 변수를 강제로 캐스팅할 필요가 없다.
자바에서 익숙하게 사용해 온 개념을 코틀린으로 바꾸면 보통 코틀린이 더 간결하고 읽기 좋은 코드를 만들어낸다. 확장을 통해 자바 라이브러리를 활용하면 코틀린과 자바를 함께 쓰는 프로젝트에서 코틀린의 장점을 최대한 살릴 수 있다.
코틀린에서 컬렉션 만들기
val set = hashSetOf(1, 7, 53)
비슷한 방법으로 리스트와 맵도 만들 수 있다.
val list = arrayListOf(1, 7, 53) val map = hashMapOf(1 to “one”, 7 to “seven”, 53 to “fifty-three”)
여기서 to가 언어가 제공하는 특별한 키워드가 아니라 일반 함수라는 점에 유의해야 한다.
>>> println(set.javaClass) class java.util.HashSet >>> println(list.javaClass) class java.util.ArrayList >>> println(map.javaClass) class java.util.HashMap
위 결과는 코틀린이 자신만의 컬렉션 기능을 제공하지 않는다는 의미이다. 코틀린이 자체 컬렉션을 제공하지 않는 이유는 뭘까? 표준 자바 컬렉션을 활용하면 자바 코드와 상호작용하기가 훨씬 더 쉽다. 자바에서 코틀린 함수를 호출하거나 코틀린에서 자바 함수를 호출할 때 자바와 코틀린 컬렉션을 서로 변환할 필요가 없다.
코틀린 컬렉션은 자바 컬렉션과 똑같은 클래스다. 하지만 코틀린에서는 자바보다 더 많은 기능을 쓸 수 있다. 예를 들어 리스트의 마지막 원소를 가져오거나 수로 이뤄진 컬렉션에서 최댓값을 찾을 수 있다.
>>> val strings = listOf(“first”, “second”, “fourteenth”) >>> println(strings.last()) fourteenth >>> val numbers = setOf(1, 14, 2) >>> println(numbers.max()) 14
함수를 호출하기 쉽게 만들기
자바 컬렉션에는 디폴트 toString 구현이 들어있다. 하지만 그 디폴트 toString의 출력 형식은 고정돼 있고 필요한 형식이 아닐수 있다. 디폴트 구현이 아닌 다른 형식을 출력하고 싶을땐 어떻게 해야할까? 코틀린에서는 이런 요구사항을 처리할 수 있는 함수가 표준 라이브러리에 이미 들어있다.
처음에는 함수 선언을 간단하게 만들수 있게 코틀린이 지원하는 여러 기능을 사용하지 ㅇ낳고 함수를 직접 구현한다. 그 후에 좀더 코틀린답게 같은 함수를 다시 구현한다. 다음 리스트의 joinToString 함수는 컬렉션 원소를 StringBuilder의 뒤에 덧붙인다. 이때 원소 사이에 구분자를 추가하고, StringBuilder의 맨 앞과 맨 뒤에는 prefix와 postfix를 추가한다.
fun
joinToString( collection: Collection , separator: String, prefix: String, postfix: String ): String { val result = StringBuilder(prefix) for ((index, element) in collection.withIndex()) { if (index > 0) result.append(separator) // 첫 원소 앞에는 구분자를 붙이면 안된다. result.apppend(element) } result.append(postfix) return result.toString() } 이 함수는 제네릭(generic)하다. 즉, 이 함수는 어떤 타입의 값을 원소로 하는 컬렉션이든 처리할 수 있다. 제네릭 함수의 문법은 자바와 비슷하다. 이 함수를 그대로 써도 좋지만, 선언 부분을 좀더 고민해봐야한다.
코틀린으로 작성한 함수를 호출할 때는 함수에 전달하는 인자 중 일부(또는 전부)의 이름을 명시할 수 있다. 호출 시 인자 중 어느 하나라도 이름을 명시하고 나면 혼동을 막기 위해 그 뒤에 오는 모든 인자는 이름을 꼭 명시해야 한다.
함수의 디폴트 파라미터 값은 함수를 호출하는 쪽이 아니라 함수 선언 쪽에서 지정된다. 따라서 어떤 클래스 안에 정의된 함수의 디폴트 값을 바꾸고 그 클래스가 포함된 파일을 재컴파일하면 그 함수를 호출하는 코드 중에 값을 지정하지 않은 모든 인자는 자동으로 바뀐 디폴트 값을 적용받는다.
디폴트 값과 자바
자바에는 디폴트 파라미터 값이라는 개념이 없어서 코틀린 함수를 자바에서 호출하는 경우에는 그 코틀린 함수가 디폴트 파라미터 값을 제공하더라도 모든 인자를 명시해야 한다. 자바에서 코를니 함수를 자주 호출해야 한다면 자바 쪽에서 좀더 편하게 코틀린 함수를 호출하고 싶을 것이다. 그럴때 @JvmOverloads 어노테이션을 함수에 추가할 수 있다. @JvmOverloads를 함수에 추가하면 코틀린 컴파일러가 자동으로 맨 마지막 파라미터로부터 파라미터를 하나씩 생략한 오버로딩한 자바 메소드를 추가해준다. 각각의 오버로딩한 함수들은 시그니처에서 생략된 파라미터에 대해 코틀린 함수의 디폴트 파라미터 값을 사용한다.
1. 정적인 유틸리티 클래스 없애기: 최상위 함수와 프로퍼티
자바에서는 모든 코드를 클래스의 메소드로 작성해야 한다. 보통 그런 구조는 잘 작동한다. 하지만 실전에서는 어느 한 클래스에 포함시키기 어려운 코드가 많이 생긴다. 일부 연산에는 비슷하게 중요한 역할을 하는 클래스가 둘 이상 있을 수도 있다. 그 결과 다양한 정적 메소드를 모아두는 역할만 담당하며, 특별한 상태나 인스턴스 메소드는 없는 클래스가 생겨난다. JDK의 Collections 클래스가 전형적인 예다. 코틀린에서는 이런 무의미한 클래스가 필요없다. 대신 함수를 직접 소스 파일의 최상위 수준, 모든 다른 클래스의 밖에 위치시키면 된다. 그런 함수들은 여전히 그 파일의 맨 앞에 정의된 패키지의 멤버 함수이므로 다른 패키지에서 그함수를 사용하고 싶을때는 그 함수가 정의된 패키지를 임포트해야만 한다. 하지만 임포트 시 유틸리티 클래스 이름이 추가로 들어갈 필요는 없다.
joinToString 함수를 strings 패키지에 직접 넣어본다. join.kt라는 파일을 다음과 같이 작성한다.
package strings fun joinToString(…): String { … }
JVM이 클래스 안에 들어있는 코드만을 실행할 수 있기 때문에 컴파일러는 이 파일을 컴파일할 때 새로운 클래스를 정의해준다. 코틀린만 사용하는 경우에는 그냥 그런 클래스가 생긴다는 사실만 기억하면 된다. 하지만 함수를 자바 등의 다른 JVM 언어에서 호출하고 싶다면 코드가 어떻게 컴파일되는지 알아야 joinToString과 같은 최상위 함수를 사용할 수 있다. 어떻게 코틀린이 join.kt를 컴파일하는지 보여주기 위해 join.kt를 컴파일한 결과와 같은 클래스를 자바 코드로 써보면 다음과 같다.
/* 자바 */ package strings; public class JoinKt { // join.kt 파일에 해당하는 클래스 public static String joinToSTring(…) { … } }
코틀린 컴파일러가 생성하는 클래스의 이름은 최상위 함수가 들어있던 코틀린 소스파일의 이름과 대응한다. 코틀린 파일의 모든 최상위 함수는 이클래스의 정적인 메소드가 된다. 따라서 자바에서 joinToString을 호출하기는 쉽다.
/* 자바 */ import strings.JoinKt; … JoinKt.joinToString(list, “, “, “”, “”);
파일에 대응하는 클래스의 이름 변경하기
코틀린 최상위 함수가 포함되는 클래스의 이름을 바꾸고 싶다면 파일에 @JvmName 어노테이션을 추가한다. @JvmName 어노테이션은 파일의 맨 앞, 패키지 이름 선언 이전에 위치해야 한다.
@file:JvmName(“StringFunctions”) // 클래스 이름을 지정하는 어노테이션 package strings // @file:JvmName 어노테이션 뒤에 패키지문이 와야한다. fun joinToString(…): String { … }
이제 다음과 같이 joinToString 함수를 호출할 수 있다.
/* 자바 */ import strings.StringFunctions; StringFunctions.joinToString(list, “, “, “”, “”);
최상위 프로퍼티
함수와 마찬가지로 프로퍼티도 파일의 최상위 수준에 놓을 수 있다. 어떤 데이터를 클래스 밖에 위치시켜야 하는 경우는 흔하지는 않지만, 그래도 가끔 유용할때가 있다. 예를 들어 어떤 연산을 수행하는 횟수를 저장하는 var 프로퍼티를 만들 수도 있다.
var opCount = 0 // 최상위 프로퍼티를 선언한다. fun performOperation() { opCount++ // 최상위 프로퍼티의 값을 변경한다. } fun reportOperationCount() { println(“Operation performed ${opCount} times”) // 최상위 프로퍼티의 값을 읽는다. }
이런 프로퍼티의 값은 정적 필드에 저장된다. 최상위 프로퍼티를 활용에 코드에 상수를 추가할 수도 있다.
val UNIX_LINE_SEPARATOR = ”
”
기본적으로 최상위 프로퍼티도 다른 모드 프로퍼티처럼 접근자 메소드를 통해 자바 코드에 노출된다(val의 경우 게터, var의 경우 게터와 세터가 생긴다). 겉으론 상수처럼 보이는데, 실제로는 게터를 사용해야 한다면 자연스럽지 못하다. 더 자연스럽게 사용하려면 이 상수를 public static final 필드로 컴파일해야 한다. const 변경자를 추가하면 프로퍼티를 public static final 필드로 컴파일하게 만들 수 있다. (단, 원시 타입과 String 타입의 프로퍼티만 const로 지정할 수 있다)
const val UNIX_LINE_SEPARATOR = ”
”
앞의 코드는 다음 자바 코드와 동등한 바이트코드를 만들어낸다.
/* 자바 */ public static final String UNIX_LINE_SEPARATOR = ”
“;
메소드를 다른 클래스에 추가: 확장 함수와 확장 프로퍼티
기존 코드와 코틀린 코드를 자연스럽게 통합하는 것은 코틀린의 핵심 목표 중 하나다. 완전히 코틀린만으로만 이뤄진 프로젝트조차도 JDK나 안드로이드 프레임워크 또는 다른 서드파티 프레임워크 등의 자바 라이브러리를 기반으로 만들어진다. 또 코틀린은 기존 자바 프로젝트에 통합하는 경우에는 코틀린으로 직접 변환할 수 없거나 미처 변환하지 않은 기존 자바 코드를 처리할 수 있어야 한다. 이런 기존 자바 API를 재작성하지 않고도 코틀린이 제공하는 여러 편리한 기능을 사용할 수 있다면 좋을 것이다. 바로 확장 함수(extension function)가 그런 역할을 해줄수 있다.
개념적으로 확장함수는 단순하다. 확장 함수는 어떤 클래스의 멤버 메소드인 것처럼 호출할 수 있지만 그 클래스의 밖에서 선언된 함수다. 확장함수를 보여주기 위해 어떤 문자열의 마지막 묵자를 되돌려주는 메소드를 추가해본다.
pckage strings fun String.lastChar() : Char = this.get(this.length – 1)
확장 함수를 만들려면 추가하려는 함수 이름 앞에 그 함수가 확장할 클래스의 이름을 덧붙이기만 하면 된다. 클래스 이름을 수신 객체 타입(receiver type)이라 부르며, 확장 함수가 호출되는 대상이 되는 값(객체)을 수신 객체(receiver object)라고 부른다. 수신 객체 타입은 확장이 정의될 클래스의 타입이며, 수신 객체는 그 클래스에 속한 인스턴스 객체다.
이 함수를 호출하는 구문은 다른 일반 클래스 멤버를 호출하는 구문과 똑같다.
println(“Kotlin”.lastChar())
이 예제에서는 String이 수신 객체 타입이고 “kotlin”이 수신 객체다. 어떤 면에서 이는 String 클래스에 새로운 메소드를 추가하는 것과 같다. String 클래스가 여러분이 직접 작성하 코드가 아니고 심지어 String 클래스의 소스코드를 소유한 것도 아니지만, 원하는 메소드를 String 클래스에 추가할 수 있다. 심지어 String이 자바나 코틀린 등의 언어 중 어느것으로 작성됐는가는 중요하지 않다. 예를 들어 Groovy와 같은 다른 JVM 언어로 작성된 클래스도 확장할 수 있다. 자바 클래스로 컴파일한 클래스 파일이 있는 한 그 클래스에 원하는 대로 확장을 추가할 수 있다.
일반 메소드의 본문에서 this를 사용할 때와 마찬가지로 확장 함수 본문에도 this를 쓸 수 있다. 그리고 일반 메소드와 마찬가지로 확장 함수 본문에서도 this를 생략할 수 있다.
package strings fun String.lastChar(): Char = get(length – 1) // 수신 객체 멤버에 this 없이 접근할 수 있다
확장 함수 내부에서는 일반적인 인스턴스 메소드의 내부에서와 마찬가지로 수신 객체의 메소드나 프로퍼티를 바로 사용할 수 있다. 하지만 확장 함수가 캡슐화를 깨지는 않는다. 클래스 안에서 정의한 메소드와 달리 확장 함수 안에서는 클래스 내부에서만 사용할 수 있는 private 멤버나 protected 멤버를 사용할 수 없다. 이제부터는 클래스의 멤버 메소드와 확장 함수를 모두 메소드라고 부르게 된다. “확장 함수 내부에서는 수신 객체의 모든 메소드를 호출할 수 있다”라고 말하면 확장 함수 내부에서 수신 객체의 멤버 메소드와 확장 함수를 모두 호출할 수 있다는 뜻이다. 호출하는 쪽에서는 확장 함수와 멤버 메소드를 구분할 수 없다. 그리고 호출하는 메소드가 확장 함수인지 멤버 메소드인지 여부가 중요한 경우도 거의 없다. 확장 함수는 코틀린 문법상 반드시 짧은 이름을 써야 한다. 따라서 임포트 시 이름을 바꾸는 것이 확장 함수의 이름 충돌을 해결하는 유일한 방법이다.
1. 자바에서 확장 함수 호출
내부적으로 확장 함수는 수신 객체를 첫번째 인자로 받는 정적 메소드이다. 그래서 확장 함수를 호출해도 다른 어댑터 객체나 실행 시점 부가 비용이 들지 않는다. 이런 설계로 인해 자바에서 확장함수를 사용하기도 편하다. 단지 정적 메소드를 호출하면서 첫번째 인자로 수신 객체를 넘기기만 하면 된다. 다른 최상위 함수와 마찬가지로 확장 함수가 들어있는 자바 클래스 이름도 확장 함수가 들어있는 파일 이름에 따라 결정된다. 따라서 확장 함수를 StringUtil.kt 파일에 정의했다면 다음과 같이 호출할 수 있다.
/* 자바 */ char c = StringUtilKt.lastChar(“Java”);
확장 함수로 유틸리티 함수 정의
이제 joinToString 함수의 최종 버전을 만든다. 이제 이 함수는 코틀린 라이브러리가 제공하는 함수와 거의 같아졌다.
fun
Collection .joinToString( // Collection 에 대한 확장 함수를 선언한다. separator: String = “, “, prefix: String = “”, postfix: String = “” ): String { val result = StringBuilder(prefix) for ((index, element) in this.withIndex()) // this는 수신 객체를 가리킨다. 여기서는 T 타입의 원소로 이뤄진 컬렉션이다. if (index > 0) result.append(separator) result.append(element) } result.append(postfix) return result.toString() } 확장 함수는 단지 정적 메소드 호출에 대한 문법적인 편의(syntatic sugar)일 뿐이다. 그래서 클래스가 아닌 더 구체적인 타입을 수신객체 타입으로 지정할 수도 있다. 그래서 문자열의 컬렉션에 대해서만 호출할 수 있는 join 함수를 지정하고 싶다면 다음과 같이하면 된다.
fun Collection
.join( separator: String = “, “, prefix: String = “”, postfix: String = “” ) = joinToString(separator, prefix, postfix) 확장 함수가 정적 메소드와 같은 특징을 가지므로, 확장 함수를 하위 클래스에서 오버라이드할 수는 없다.
실행 시점에 객체 타입에 따라 동적으로 호출될 대상 메소드를 결정하는 방식을 동적 디스패치(dynamic dispatch)라고 한다. 반면 컴파일 시점에 알려진 변수 타입에 따라 정해진 메소드를 호출하는 방식을 정적 디스패치(static dispatch)라고 부른다. 프로그래밍 언어 용어에서 ‘정적’이란느 말은 컴파일 시점을 의미하고, ‘동적’이라는 말은 실행 시점을 의미한다.
확장 함수는 클래스의 일부가 아니다. 확장 함수는 클래스 밖에 선언된다. 이름과 파라미터가 완전히 같은 확장 함수를 기반 클래스와 하위 클래스에 대해 정의해도 실제로는 확장 함수를 호출할때 수신 객체로 지정한 변수의 정적 타입에 의해 어떤 확장함수가 호출될지 결정되지, 그 변수에 저장된 객체의 동적인 타입에 의해 확장함수가 결정되지 않는다.
확장 함수를 오버라이드할 수는 없다. 코틀린은 호출될 확장 함수를 정적으로 결정하기 때문이다.
어떤 클래스를 확장한 함수와 그 클래스의 멤버 함수의 이름과 시그니처가 같다면 확장 함수가 아니라 멤버 함수가 호출된다(멤버 함수의 우선순위가 더 높다). 클래스의 API를 변경할 경우 항상 이를 염두에 둬야 한다. 코드 소유권을 가진 클래스에 대한 확장함수를 정의해서 사용하는 외부 클라이언트 프로젝트가 있다고 한다. 그 확장 함수와 이름과 시그니처가 같은 멤버 함수를 클래스 내부에 추가하면 클라이언트 프로젝트를 재컴파일한 순간부터 그 클라이언트는 확장 함수가 아닌 새로 추가된 멤버 함수를 사용하게 된다.
확장 프로퍼티
확장 프로퍼티를 사용하면 기존 클래스 객체에 대한 프로퍼티 형식의 구문으로 사용할 수 있는 API를 추가할 수 있다. 프로퍼티라는 이름으로 불리기는 하지만 상태를 저장할 적절한 방법이 없기 때문에(기존 클래스의 인스턴스 객체에 필드를 추가할 방법은 없다) 실제로 확장 프로퍼티는 아무 상태도 가질 수 없다. 하지만 프로퍼티 문법으로 더 짧게 코드를 작성할 수 있어서 편한 경우가 있다.
val String.lastChar: Char get() = get(length – 1)
확장 함수의 경우와 마찬가지로 확장 프로퍼티도 일반적인 프로퍼티와 같은데, 단지 수신 객체 클래스가 추가됐을 뿐이다. 뒷받침하는 필드가 없어서 기본 게터 구현을 제공할 수 없으므로 최소한 게터는 꼭 정의를 해야 한다. 마찬가지로 초기화 코드에서 계산한 값을 담을 장소가 전혀 없으므로 초기화 코드도 쓸수 없다.
StringBuilder에 같은 프로퍼티를 정의한다면 StringBuilder의 맨 마지막 문자는 변경 가능하므로 프로퍼티를 var로 만들수 있다.
var StringBuilder.lastChar: Char get() = get(length – 1) // 프로퍼티 게터 set(value: Char) { this.setCharAt(length – 1, value) // 프로퍼티 세터 }
코틀린에서 확장 프로퍼티를 사용하는 방법은 멤버 프로퍼티를 사용하는 방법과 같다. 자바에서 확장 프로퍼티를 사용하고 싶다면 항상 StringUtilKt.getLastChar(“Java”) 처럼 게터나 세터를 명시적으로 호출해야 한다.
가변 인자 함수: 인자의 갯수가 달라질 수 있는 함수 정의
리스트를 생성하는 함수를 호출할 때 원하는 만큼 원소를 전달할 수 있다.
val list = listOf(2, 3, 5, 7, 11)
라이브러리에서 이 함수의 정의를 보면 다음과 같다.
fun listOf
(vararg values: T): List { … } 자바의 가변 길이 인자(varargs)에 보다 익숙할 것이다. 가변 길이 인자는 메소드를 호출할 때 원하는 갯수만큼 값을 인자로 넘기면 자바 컴파일러가 배열에 그 값들을 넣어주는 기능이다. 코틀린의 가변 길이 인자도 자바와 비슷하다. 다만 문법이 조금 다르다. 타입 뒤에 …를 붙이는 대신 코틀린에서는 파라미터 앞에 vararg 변경자를 붙인다.
이미 배열에 들어있는 원소를 가변 길이 인자로 넘길 때도 코틀린과 자바 구문이 다르다. 자바에서는 배열을 그냥 넘기면 되지만 코틀린에서는 배열을 명시적으로 풀어서 배열의 각 원소가 인자로 전달되게 해야 한다. 기술적으로는 스프레드(spread) 연산자가 그런 작업을 해준다. 하지만 실제로는 전달하려는 배열 앞에 *를 붙이기만 하면 된다.
fun main(args: Array
) { val list = listOf(“args: “, *args) // 스프레드 연산자가 배열의 내용을 펼쳐준다. println(list) } 이 예제는 스프레드 연산자를 통하면 배열에 들어있는 값과 다른 여러 값을 함께 써서 함수를 호출할 수 있음을 보여준다. 이런 기능은 자바에서는 사용할 수 없다. 이제 맵으로 대상을 옮겨서 코틀린 함수 호출의 가독성을 향상시킬 수 있는 다른 방법인 중위 호출에 대해 살펴본다.
값의 쌍 다루기: 중위 호출과 구조 분해 선언
맵을 만들려면 mapOf 함수를 사용한다.
val map = mapOf(1 to “one”, 7 to “seven”, 53 to “fifty-three”)
여기서 to라는 단어는 코틀린 키워드가 아니다. 이 코드는 중위 호출(infix call)이라는 특별한 방식으로 to라는 일반 메소드를 호출한 것이다.
중위 호출 시에는 수신 객체와 유일한 메소드 인자 사이에 메소드 이름을 넣는다. (이때 객체, 메소드 이름, 유일한 인자 사이에는 공백이 들어가야 한다). 다음 두 호출은 동일하다.
1.to(“one”) // “to” 메소드를 일반적인 방식으로 호출함 1 to “one” // “to” 메소드를 중위 호출 방식으로 호출함
인자가 하나뿐인 메소드나 인자가 하나뿐인 확장 함수에 중위 호출을 사용할 수 있다. 함수(메소드)를 중위 호출에 사용하게 허용하고 싶으면 infix 변경자를 함수 선언 앞에 추가해야 한다. 다음은 to 함수의 정의를 간략하게 줄인 코드다.
infix fun Any.to(other: Any) = Pair(this, other)
이 to 함수는 Pair의 인스턴스를 반환한다. Pair는 코틀린 표준 라이브러리 클래스로 그 이름대로 두 원소로 이뤄진 순서쌍을 표현한다. 실제로 to는 제네릭 함수지만 여기서는 설명을 위해 그런 세부 사항을 생략했다.
Pair의 내용으로 두 변수를 즉시 초기화할 수 있다.
val (number, name) = 1 to “one”
이런 기능을 구조 분해 선언(destructuring declaration)이라고 부른다. 다음은 Pair에 대해 구조 분해가 어떻게 작동하는지 보여준다.
Pair 인스턴스 외 다른 객체에도 구조 분해를 적용할 수 있다. 예를 들어 key와 value라는 두 변수를 맵의 원소를 사용해 초기화할 수 있다. 루프에서도 구조 분해 선언을 활용할 수 있다. joinToString에서 본 withIndex를 구조 분해 선언과 조합하면 컬렉션 원소의 인덱스와 값을 따로 변수에 담을 수 있다.
for ((index, element) in collection.withIndex()) { println(“${index}: ${element}”) }
to 함수는 확장함수다. to를 사용하면 타입과 관계없이 임의의 순서쌍을 만들 수 있다. 이는 to의 수신 객체가 제네릭하다는 뜻이다. 1 to “one”, “one” to 1, list to list.size() 등의 호출이 모두 잘 작동한다. mapOf 함수의 선언을 살펴보자.
fun
mapOf(vararg values: Pair ): Map listOf와 마찬가지로 mapOf에도 원하는 갯수만큼 인자를 전달할 수 있다. 하지만 mapOf의 경우에는 각 인자와 키와 값으로 이뤄진 순서쌍이어야 한다.
코틀린을 잘 모르는 사람이 보면 새로운 맵을 만드는 구문은 코틀린이 맵에 대해 제공하는 특별한 문법인 것처럼 느껴진다. 하지만 실제로는 일반적인 함수를 더 간결한 구문으로 호출하는 것뿐이다.
공식 문서 기반의 Kotlin 한글 정리
이 글은 https://kotlinlang.org/docs/reference/ 의 공식문서를 참고해서 한글로 차근차근 공부하며 정리한 글입니다. 아무쪼록 도움이 되시길 바라겠습니다.
Kotlin 컴파일 속도
대개 Java보다 조금 빠르다!
클린 빌드 시에는 Java보다 조금 느리지만, 일반적인 개발 시나리오의 증분 빌드 시 성능이 좋다.
Kotlin vs Java: Compilation speed
세미콜론 제거
fun main(args: Array < String >) { val age = 30 val name: String = “Seo Jaeryong” println(name.length) }
변수는 모두 val 아니면 var
val : 변경 불가능 (read-only)
fun main(args: Array < String >) { val age: Int = 30 a = 20 }
var : 변경 가능 (read-write)
fun main(args: Array < String >) { var age: Int = 30 a = 20 }
함수 값 리턴의 간략화
일반적인 함수
fun maxOf( a : Int, b: Int): Int { if ( a > b) { return a } else { return b } }
간략하게 표현
fun maxOf( a : Int, b: Int): Int = if ( a > b) a else b
더 간략하게 표현 (리턴 타입을 생략해도 추론 가능)
fun maxOf( a : Int, b: Int) = if ( a > b) a else b
Nullsafe
Kotlin은 널 참조의 위험(The Billion Dollar Mistake)을 제거하기 위해 노력했다.
아래와 같은 상황에서만 NPE(NullPointerException)를 만들 수 있다.
명시적인 throw NullPointerException() 호출 !! 오퍼레이터 사용 NPE를 발생시키는 외부 Java 코드 호출
Non-null
var a: String = “abc” a = null
Nullable(?)
var b: String ? = “abc” b = null
‘?.’ operator (for nullable)
var b: String ? = “abc” b.length b?.length
여러 체인의 객체를 호출할 때 유용하다.
String name; if (bob != null ) { if (department != null ) { if (head != null ) { name = bob.department.head.name; } } } var name: String = bob?.department?.head?.name ?: “”
!! operator (for nullable)
for NPE-lovers .
operator (for nullable)
var b : String? = null val l = b!!.length // ok, but throw an NPE if b is null
Safe casts
val aInt: Int? = a as? Int // return `n ull ` if the attempt was not successful
Collections of Nullable Type
val nullableList: List
= listOf( 1 , 2 , null , 4 ) val intList: List = nullableList.filterNotNull() 접근자
public (default) : 전역 프로젝트에 공개
: 전역 프로젝트에 공개 private : 같은 파일내에 공개
: 같은 파일내에 공개 protected : Subclasses에 공개
: Subclasses에 공개 internal : 같은 Module내에 공개
Module이란? – IntelliJ an IntelliJ IDEA module – a Maven project – a Gradle source set – a set of files compiled with one invocation of the Ant task.
자동 형변환 (Smart Casts)
is 체크 후 (Java의 instanceof)
fun demo(x: Any) { if (x is String) { print(x.length) } }
null 체크 후
fun demo1(x: String ?) { if (x != null ) { demo2(x) } } fun demo2(x: String ) { print(x.length) }
For-Loop
List
val items = listOf( “apple” , “banana” , “kiwi” ) for (index in items.indices) { println( “item at $index is ${items[index]}” ) } 결과 item at 0 is apple item at 1 is banana item at 2 is kiwi
Range (a…b)
for (i in 0. .10 ) { print (i) } 결과 012345678910
While-Loop
val items = listOf( “apple” , “banana” , “kiwi” ) var index = 0 while (index < items.size) { println( "item at $index is ${items[index]}" ) index++ } 결과 item at 0 is apple item at 1 is banana item at 2 is kiwi When 다양한 타입 비교 when (obj) { 1 -> “One” “Hello” -> “Greeting” is Long -> “Long” ! is String -> “Not a string” else -> “Unknown” }
{} 블록을 지정해서 작성
when (x) { 1 -> print ( “x == 1” ) 2 -> print ( “x == 2” ) else -> { // Note the block print ( “x is neither 1 nor 2” ) } }
한 조건에 여러 값을 비교 (0, 1)
when (x) { 0 , 1 -> print ( “x == 0 or x == 1” ) else -> print ( “otherwise” ) }
범위 비교
when (x) { in 1. .10 -> print ( “x is in the range” ) in validNumbers -> print ( “x is valid” ) ! in 10. .20 -> print ( “x is outside the range” ) else -> print ( “none of the above” ) }
infix notation(중위 표기법)
함수앞에 infix를 붙인다.
멤버함수 혹은 확장 함수(extension funtions)에 사용
하나의 파라미터를 받는 함수에서 사용
// 정의 방법 infix fun Int.shl(x: Int): Int { … } 1. shl( 2 ) 1 shl 2 // can also be called like this
Extensions
클래스에 함수 확장
// ViewExt .kt fun View .show () { visibility = View .VISIBLE } fun View .hide () { visibility = View .GONE } // SearchActivity .kt var textView = findViewById(R .id .textView ) as TextView textView .show () // ok textView .hide () // ok
클래스
기본
class Invoice { }
바디가 없을 때 {} 생략가능
class Empty
생성자 표현 (constructor 키워드)
class Person constructor (firstName: String )
생성자 표현에 constructor 키워드 생략 가능
class Person ( firstName : String ) { }
생성자의 초기화 블록 지정 (init 키워드)
class Customer (name: String) { init { logger.info( “Customer initialized with value ${name}” ) } }
위 표현을 아래와 같이 표현 가능 (동일)
class Customer { constructor( name: String ) { logger.info( “Customer initialized with value ${name}” ) } }
@Inject 어노테이션이 필요하면 constructor 키워드가 필요하다.
class Customer public @Inject constructor (name: String )
Data 클래스
모든 var, val 변수의 Getter 제공 모든 var 변수의 Setter를 제공 equals() / hashCode() / toString() / copy() 구현을 아름답게 제공
data class Customer ( val name : String , var email : String )
Open 클래스
코틀린의 모든 클래스는 기본적으로 final이라 상속이 불가능하다. open 키워드를 class 앞에 붙여줌으로써 상속을 허용시킨다.
open class Base (p: Int ) class Derived ( p : Int ) : Base ( p )
Abstract 클래스 (open 붙일 필요 없음)
abstract class Base { abstract fun f() } class Derived() : Base() { override fun f() { // … } }
Nested 클래스
Outer클래스 멤버 참조가 불가능
class Outer { private val bar: Int = 1 class Nested { fun foo() = 2 } } val demo = Outer.Nested().foo()
Inner 클래스
Outer클래스 멤버 참조 가능
class Outer { private val bar: Int = 1 inner class Inner { fun foo() = bar } } val demo = Outer().Inner().foo()
익명 Inner 클래스
object 키워드를 사용하고 타입은 interface 나 abstract class 를 받는다. interface : 이름 뒤에 ()를 붙이지 않는다. View.OnClickListener abstract class : 이름 뒤에 ()를 붙인다. SimpleOnQueryTextListener()
// interface button.setOnClickListener(object : View.OnClickListener { override fun onClick(view: View) { // … } }) // abstract class searchView.setOnQueryTextListener(object : SimpleOnQueryTextListener() { override fun onQueryTextSubmit(query: String): Boolean { presenter.searchImage(query) return false } })
Enum 클래스
enum class Direction { NORTH, SOUTH, WEST, EAST } enum class Color (val rgb: Int) { RED( 0xFF0000 ), GREEN( 0x00FF00 ), BLUE( 0x0000FF ) }
클래스 위임 (Class Delegation)
해당 클래스안에 by절 뒤 에 오는 참조가 private으로 저장 된다. 해당 클래스안에 by절 앞 에 오는 인터페이스의 메소드를 자동 생성 한다.
interface Base { fun print() } class BaseImpl ( val x : Int ) : Base { override fun print () { print ( x ) } } // b가 Derived 내에 private으로 저장 됨 // Base 의 메소드를 Derived 내에 자동 생성한다. // 그 메소드들은 b를 참조하여 실행한다. class Derived ( b : Base ) : Base by b fun main ( args : Array < String >) { val b = BaseImpl (10) Derived ( b ) .print () // prints 10 }
Destructuring Declarations
// class data class Person(val name: String, val age: Int) val (name, age) = Person( “Jee-ryong” , 30 ) print ( “My name is $name and I am $age years old.” ) // map for ((key, value) in map ) { print ( “key is $key ” ) print ( “value is $value ” ) }
List
mutableListOf, arrayListOf 둘 다 ArrayList를 만들어 리턴
ArrayList보다 kotlin스타일로 리스트를 다루는 인터페이스가 구현되어 있는 MutableList를 사용하는 편이 더 나아보임.
val lists: List
= listOf( 1 , 2 , 3 ) val lists: MutableList = mutableListOf( 1 , 2 , 3 ) val lists: ArrayList = arrayListOf( 1 , 2 , 3 ) Map
// new instance val map = mapOf( “Korea” to 1 , “Japan” to 2 ) // read only val map = mutableMapOf( “Korea” to 1 , “Japan” to 2 ) // read/write val map = linkedMapOf( “Korea” to 1 , “Japan” to 2 ) val map = hashMapOf( “Korea” to 1 , “Japan” to 2 ) val map = sortedMapOf( “Korea” to 1 , “Japan” to 2 ) // use map .put( “London” , 3 ) map .get( “Korea” ) map [ “Korea” ] map .containsKey( “Japan” ) map .toList() map .toMap() map .toMutableMap() map .toSortedMap()
Set
// new instance val set = setOf( “Korea” , “Japan” ) val set = mutableSetOf( “Korea” , “Japan” ) val set = hashSetOf( “Korea” , “Japan” ) val set = linkedSetOf( “Korea” , “Japan” ) val set = sortedSetOf( “Korea” , “Japan” ) // use set .add ( “London” ) set .remove ( “London” ) set .contains ( “London” ) set .size set .toList () set .toMutableList () set .toSet () set .toHashSet () set .toMutableSet () set .toSortedSet ()
Ranges
for (i in 1 .. 4 ) print(i) for (i in 1 .. 4 step 2 ) print(i) for (i in 4 downTo 1 ) print(i) for (i in 4 downTo 1 step 2 ) print(i) for (i in 1 until 10 ) println(i) ( 1 .. 12 step 2 ).last
Equality
Referential equality ( === , !== )
val a = Integer( 10 ) val b = a a === b a !== b val a = Integer( 10 ) val b = Integer( 10 ) a === b a !== b
Structural equality ( == , != )
data class Person( val name: String, val age: Int) val person = Person( “Jae-ryong” , 20 ) val person2 = Person( “Jae-ryong” , 20 ) person == person2 person != person2
Arrays Equality (using infix funtions)
contentEquals
val hobbies = arrayOf( “Hiking” , “Chess” ) val hobbies2 = arrayOf( “Hiking” , “Chess” ) assertTrue(hobbies contentEquals hobbies2) public infix inline fun
kotlin.Array .contentEquals(other: kotlin.Array ): kotlin.Boolean Lambdas
// example fun
List .map (transform: (T) -> R) : List < R > { val result = arrayListOf < R > () for (item in this ) result . add (transform(item)) return result } input 파라미터 네이밍은 자유
var ints = listOf( 1 , 2 , 3 , 4 , 5 ) val doubled = ints.map { value -> value * 2 }
it 을 사용하면 input 파라미터 생략가능
ints.map { it * 2 }
사용하지 않는 파라미터는 _ 로 선언 가능
var map = mapOf( “Korea” to “Seoul” , “Japan” to “Tokyo” ) map . forEach { _, value -> println( “$value!” ) }
키워드에 대한 정보 kotlin in action 한글 pdf
다음은 Bing에서 kotlin in action 한글 pdf 주제에 대한 검색 결과입니다. 필요한 경우 더 읽을 수 있습니다.
이 기사는 인터넷의 다양한 출처에서 편집되었습니다. 이 기사가 유용했기를 바랍니다. 이 기사가 유용하다고 생각되면 공유하십시오. 매우 감사합니다!
사람들이 주제에 대해 자주 검색하는 키워드 How to make Pdf Reader App | Show pdf file from storage | Android Studio | Kotlin
- How to make Pdf Reader App
- Show pdf file from storage
- Android Studio
- How to make Pdf Reader App | Show pdf file from storage | Android Studio | Kotlin
- pdf viewer
- pdf reader
- kotlin
- 2020
How #to #make #Pdf #Reader #App #| #Show #pdf #file #from #storage #| #Android #Studio #| #Kotlin
YouTube에서 kotlin in action 한글 pdf 주제의 다른 동영상 보기
주제에 대한 기사를 시청해 주셔서 감사합니다 How to make Pdf Reader App | Show pdf file from storage | Android Studio | Kotlin | kotlin in action 한글 pdf, 이 기사가 유용하다고 생각되면 공유하십시오, 매우 감사합니다.