REPL 사용하기
Tools -> Kotlin -> Kotlin REPL(Read-eval-print loop) 을 선택
윈도우 에서는 Ctrl + Enter를 사용하여 실행하고, 맥에서는 Command + Enter를 사용하여 실행한다.
스크래치 사용하기
File -> New -> Scratch File 을 선택 후 Kotlin을 클릭
REPL처럼 한 줄 단위로 코드를 실행하는 것과는 달리 *.kts 라는 파일에 저장하여 파일 단위로 실행 할 수 있다.
문법
기본적인 문법은 다른 프로그래밍 언어와 비슷하기 때문에 특화적인 부분만 소개하기로 한다.
when
// java의 switch문 처럼 사용 가능
val x = 1
when(x){
1 -> println("x == 1") // 값 하나
2, 3 -> println("x == 2 or x == 3") // 여러 값은 콤마로
in 4..7 -> println("4 부터 7사이") // in 연산자로 범위 지정
!in 8..10 -> println("8부터 10사이가 아님")
else -> {
println("x는 1이나 2가 아님")
}
}
// 식 처럼 사용 가능
val number = 1
var numStr = when( number%2 ) {
0 -> "짝"
else -> "홀"
}
println(numStr)
// 함수의 반환 값으로도 사용 가능
fun isEven(num: Int) = when( num%2 ) {
0 -> "짝"
else -> "홀"
}
println(isEven(1))
for
for(i in 1..3) {
print(" ${i}") // 1 2 3
}
for(i in 1..10 step 2) {
print(" ${i}") // 1 3 5 7 9
}
for(i in 10 downTo 0 step 2) {
print(" ${i}") // 10 8 6 4 2 0
}
클래스 선언 및 사용
//클래스 선언
class Person {
}
// 인스턴스 생성
val person = Person()
///////////////////////////////////////////////////////////
class Person(var name: String) {
}
class Person {
constructor(name: String) {
}
}
class Person(name: String) {
init {
print(name)
}
}
///////////////////////////////////////////////////////////
// 프로퍼티
// 클래스 선언
class Person(var name: String) {
}
// 인스턴스 생성
var person = Person("멋쟁이")
person.name = "키다리" // 읽기
println(person.name) // 쓰기
접근제한자
- public (생략가능) : 전체공개. 아무것도 안 쓰면 기본적으로 public
- private : 현재 파일 내부에서만 사용 가능
- internal : 같은 모듈 내에서만 사용 가능
- protected : 상속받은 클래스에서 사용가능
class A {
val a = 1 // public
private val b = 2
protected val c = 3
internal val d = 4
}
상속
코틀린에서 기본적으로 상속은 금지되어 있다. 상속이 가능하게 하려면 open 키워드를 클래스 선언 앞에 추가해야 한다.
open class Animal {
}
class Dog: Animal() {
}
///////////////////////////////////////////////////////////////////
open class Animal(val name: String) {
}
class Dog(name: String): Animal(name) {
}
내부 클래스
class OuterClass {
var a = 10
// 내부 클래스
inner class OuterClass2 {
fun something() {
a = 20 // 접근가능
}
}
}
추상 클래스
abstract class A {
abstract fun func()
fun func2() {
}
}
class B: A() {
override fun func() {
println("hello")
}
}
val a = A() // 에러
val b = B() // OK
인터페이스
// 인터페이스 선언
interface Runnable {
// 인터페이스에서는 abstract 키워드를 생략 할 수 있다.
fun run()
// 구현된 메서드도 포함할 수 있다. 자바 8의 default 와 대응
fun fastRun() = println("빨리 달린다.")
}
// 인터페이스 구현
class Human : Runnable {
override fun run() {
println("달린다.")
}
}
//상속과 인터페이스를 함께 구현
open class Animal {
}
interface Eaterable {
fun eat()
}
// 상속은 하나의 클래스만 상속하지만 인터페이스는 콤마로 구분하여
// 여러 인터페이스를 구현할 수 있다.
class Dog: Animal(), Runable, Eatable {
override fun eat(){
println("먹는다")
}
override fun run(){
println("달린다")
}
}
var dog = Dog()
dog.run()
dog.eat()
null 가능성
val a: String // 에러 : 초기화를 반드시 해야 함
val a: String = null // 에러 : 코틀린은 기본적으로 null을 허용하지 않음
val a: String? = null // OK
lateinit 키워드로 늦은 초기화
lateinit 는 다음 조건에서만 사용가능
- var 변수에서만 사용
- null 값으로 초기화 할 수 없음
- 초기화 전에는 변수를 사용할 수 없음
- Int, Long, Double, Float에서는 사용할 수 없음
lateinit var a: String // OK
a = "hello"
println(a) // hello
lazy로 늦은 초기화
lazy는 다음 조건에서만 사용가능
- val에서만 사용 가능
var str: String by lazy {
println("초기화")
"hello"
}
println(str) // 초기화; hello
println(str) // hello
null 값이 아님을 보증( !! )
val name: String? = "키다리"
val name2: String = name // 에러
val name3: String? = name // OK
//null 값이 아님을 보증
var name4: String = name!! // OK
안전한 호출 ( ?. )
안전한 호출을 사용하면 복잡한 if 문을 한 줄로 줄일 수 있다.
val str: String? = null
var upperCase = if( str != null) str else null // null
upperCase = str?.toUpperCase // null
엘비스 연산자 ( ?: )
안전한 호출 시 null 이 아닌 기본값을 반환하고 싶을 때 사용한다.
val str: String? = null
var upperCase = if( str != null) str else null // null
upperCase = str?.toUpperCase ?: "초기화하시오" // 초기화하시오
컬렉션
// 리스트 : 배열처럼 같은 자료형의 데이터들을 순서대로 가지고 있는 자료구조
var foods: List<String> = listOf("라면", "갈비", "밥")
// 형추론으로 자료형 생략
var foods = listOf("라면", "갈비", "밥")
//요소를 변경할 경우는 mutableListOf 메서드를 사용
var foods = mutableListOf("라면", "갈비", "밥")
foods.add("초밥")
foods.removeAt(0)
foods[1] = "부대찌개" // foods.set(1, "부대찌개") 1번째 아이템을 부대찌개로 변경
println(foods) // [갈비, 부대찌개, 초밥]
println(foods[0]) // 갈비
println(foods[1]) // 부대찌개
println(foods[2]) // 초밥
//----------------------------------------------------------------
// 맵: 키와 값 쌍으로 이루어진 키가 중복될 수 없는 자료구조
// 읽기 전용 맵
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
// 변경 가능한 맵
val citiesMap = mutableMapOf("한국" to "서울", "일본" to "동경", "중국" to "북경")
citiesMap["한국"] = "서울특별시" // 요소에 덮어쓰기
citiesMap["미국"] = "워싱턴" // 추가
// 맵의 키와 값을 출력
for ((k, v) in map){
println("$k -> $v")
}
//----------------------------------------------------------------
// 셋(집합): 중복되지 않은 요소들고 구성된 자료구조
// 읽기 전용 셋
var citySet = setOf("서울", "수원", "부산")
// 수정 가능한 셋
var citySet2 = mutableSetOf("서울", "수원", "부산")
citySet2.add("안양") // [서울, 수원, 부산, 안양]
citySet2.remove("수원") // [서울, 산, 안양]
// 셋의 크기
println(citySet2.size) // 3
// '서울'이 포함되어 있는지
println(citySet2.contains("서울")) // true
람다식
람다 식은 익명 클래스나 익명 함수를 간결하게 표현 하게 해준다.
fun add(x: Int, y: Int): Int {
return x + y
}
fun add(x: Int, y: Int) = x + y
// { 인수1: 타입1, 인수2: 타입2 -> 본문 }
var add = { x: Int, y: Int -> x + y }
println(add(2, 5) // 7
SAM(Single Abstract Method) 변환
안드로이드에서 onClick() 추상 메서드 하나만 가지고 있는 View.OnClickListener 인터페이스를 익명클래스로 구현한 예이다.
button.setOnClickListener(object: View.OnClickListener {
override fun onClick(v: View?) {
// 클릭 시 처리
}
}
위의 코드는 람다 식을 이용하여 간단하게 변경할 수 있다.
button.setOnClickListener({ v: View? ->
// 클릭 시 처리
})
메서드 호출 시 맨 뒤에 전달되는 인수가 람다식인 경우 람다식을 괄호 밖으로 뺄수 있다.
button.setOnClickListener() { v: View? ->
// 클릭 시 처리
}
메서드 호출 시 전달되는 인수가 하나인 람다식인 경우 메서드의 괄호를 생략할 수 있다.
button.setOnClickListener { v: View? ->
// 클릭 시 처리
}
컴파일러가 자료형을 추론하는 경우 자료형을 생략할 수 있다.
button.setOnClickListener { v ->
// 클릭 시 처리
}
위에 인수 v를 사용하지 않고 코드를 작성할 수 있다면 다음과 같이 변경이 가능하다.
button.setOnClickListener { _ ->
// 클릭 시 처리
}
람다식에서 인수가 하나인 경우에는 아예 생략하고 블록 내에서 인수를 it로 접근할 수 있다.
button.setOnClickListener {
// 클릭 시 처리
it.visibility = View.GONE
}
기타기능
확장함수
- 기존 클래스에 상속 없이 함수를 추가가 가능
- 확장함수 내부에서는 이 객체를 this로 접근할 수 있고 이러한 객체를 리시버 객체라고 함
fun Int.isEven() = this % 2 == 0
val a = 5
val b = 6
println(a.isEven()) // false
println(b.isEven()) // true
형변환
// 숫자형 끼리는 to자료형() 메서드 사용
val a = 10L
val b = 20
val c = a.toInt() // Long -> Int
val d = b.toDouble() // Int -> Double
val e = a.toString() // Long -> String
// 숫자 형태의 문자열을 숫자로 바꿀때는 자바 메서드 사용
val intStr = "10"
var realInt = Integer.parseInt(intStr)
// 일반 클래스간의 형변환 시 as 키워드 사용
open class Animal
class Dog: Animal()
val dog = Dog()
val animal = dog as Animal
형 체크
is 키워드를 사용하여 형 체크 자바의 instanceOf와 대응
val str = "hello"
if (str is String) {
println(str.toUpperCase())
}
고차함수
함수의 인수로 함수를 전달하거나 함수를 반환하는 함수를 고차함수라고 한다.
// 인수: 숫자, 숫자, 하나의 숫자를 인수로 하는 반환값이 없는 함수
fun add(x: Int, y: Int, callback: (sum: Int) -> Unit) {
callback(x + y)
}
// 함수는 { } 로 감싸고 내부에서는 반환값을 it로 접근할 수 있음
add(5, 3, { println(it) }) // 8
동반객체(companion object)
자바의 static과 같은 정적인 메서드를 만들 수 있는 키워드 대신 companion object(동반객체) 키워드를 제공한다.
class Fragment {
companion object {
fun newInstance() : Fragment {
println("생성됨")
}
}
}
var wfragment = Fragment.newInstance()
let() 함수
- 블록에 자기 자신을 인수로 전달하고 수행된 결과를 반환한다.
- 안전한 호출연산자( ?. ) 와 함께 사용하면 null 값이 아닐 때만 실행하는 코드를 다음과 같이 나타낼 수 있다.
이 코드는 str이 null이 아닐 때만 정수로 변경하여 출력하는 코드이다.
// fun <T, R> T.let(block: (T) -> R): R
val result = str?.let { // Int
Integer.parseInt(it)
}
with() 함수
- 인수로 객체를 받고 블록에 리시버 객체로 전달한다. 그리고 수행된 결과를 반환한다.
- 리시버 객체로 전달된 객체는 this로 접근 가능하다.
- with 에 인수는 null 이 아닌경우에만 사용해야 한다.
// fun <T, R> with(receiver: T, block T.() -> R): R
// this(리시버객체)는 생략이 가능하다.
// str 은 null이 아니어야 한다.
with (str) {
println(toUpperCase())
}
apply() 함수
- 블록에 객체 자신이 리시버 객체로 전달되고 이 객체가 반환됨
- 객체의 상태를 변화시키고 그 객체를 다시 반환할 때 주로 사용한다.
// fun <T> T.apply(block: T.() -> Unit): T
val result = car?.apply {
car.setColor(Color.RED)
car.setPrice(1000)
}
run() 함수
- 익명함수처럼 사용할 때는 블록의 결과를 반환한다.
- 복잡한 계산에 임시변수가 많이 필요할때 유용하다.
// fun <R> run(block: () -> R): R
val avg = run {
val korean = 100
val english = 80
val math = 50
(korean + english + math) // 3.0
}
- 객체에서 호출하는 방법은 객체를 블록의 리시버 객체로 전달하고 블록의 결과를 반환한다.
- 안전한 호출을 할 수 있어서 with() 함수 보다는 더 유용하다.
// fun <T, R> run(block: T.() -> R): R
str?.run {
println(toUpperCase())
}
'책 > 오준석의 안드로이드 생존코딩 코틀린편' 카테고리의 다른 글
8. 수평 측정기 (0) | 2020.06.16 |
---|---|
7. 나만의 웹 브라우저 (0) | 2020.06.15 |
6. 스톱워치 (0) | 2020.06.11 |
5. 비만도 계산기 (0) | 2020.06.09 |
3. 첫 번째 앱 만들기 (0) | 2020.06.07 |