개발자 끄적끄적
코틀린(Kotlin) 기초 본문
<Kotlin>
- 편리함을 추구하는 언어
- 간결함을 추구하는 언어
- 명령형 언어, 객체지향 언어에 함수형 언어 개념
<코틀린의 특징>
- 실용성
- 이미 검증되고 많이 사용되는 언어 기능을 채택
- 간결성
- 타입 추론(타입을 명시하지 않아도 된다), getter/setter
- 안정성(Null Safety)
- Interoperability(자바와 100%호환)
- 다양한 플랫폼 지원
- JVM 기반의 서버, 안드로이드, 코틀린 자바스크립트, 코틀린-native IOS, MacOS, AndroidNDK, Windows, Linux
- 정적 타입 언어
- statically typed lang. (java, kotlin)
- dynamically typed lang. (python)
- 함수형 언어(Functional Lang)
- 오픈 소스(코틀린의 새로운 기능들을 만들수도 있다)
<패키지(Package)>
- 자바와 다르게 코틀린은 소스 파일의 위치와 이름을 정하는 데에 제한이 없다
- 한 파일에 여러 개의 클래스를 넣을 수 있다
- 패키지 구조와 디렉토리 구조가 일치할 필요가 없다
package com.example.mykotlin
/*이 파일이 com/example/mykotlin/에 있을 필요가 없다
이 파일의 이름은 마음대로 지으면 된다*/
fun main(){
println("Hello, Kotlin")
//코틀린은 세미콜론이 없어도 된다
}
//자바와 달리 main()함수가 독립적으로 존재
<변수와 값(Variables & Value)>
- 코틀린은 변수를 선언하는 키워드가 val, var
- 변수 선언 키워드 다음에 변수 명과 콜론, 그리고 타입을 쓴다
- val
- val로 선언된 변수(?) 불가 -> 초기화할 때 값을 바꿀 수 없다
- 변수라고 부르지 않고 값이라고 부른다
- val 자체가 value의 줄임말
- var
- var로 선언된 변수는 변경 가능
- variable의 줄임말
val a: Int = 10
var b: Int = 12
//a=11// compile error
b = 13 // OK
*val a = 객체
객체안에 들어있는 멤버변수의 값들을 바꾸는 것들은 가능하다
하지만 a에 들어있는 값을 바꾸는 것은 불가능
<타입 추론(Type Inference)>
- 코틀린 변수를 선언할 때 타입을 쓰지 않아도 대입되는 값에 따라 컴파일러가 알아서 타입을 정해준다
<타입(Types)>
- 코틀린은 자바와 달리 원시타입(Primitive types)와 wrapper type을 구분하지 않는다
- Byte, Short, Int, Long, Float, Double, Char, Boolean
- UByte, UShort, UInt, ULong
- 코틀린은 서로 다른 타입의 값을 자동으로 변환해주지 않기 때문에 명시적으로 값 변환을 해줘야 한다
val i = 10
val i2 : UInt = 10U
//vale lov : Long = i// Compile Error
val lov2 : Long = i.toLong() //명시적 값 변환(i.toLong()은 멤버함수 메소드)
<if문>
- 자바와 문법은 비슷하지만 코틀린에서 if는 statement가 아니라 expression(식)으로 사용 가능하다
- 즉, if문의 결과로 어떠한 값을 받을 수 있다
val x = if(a>10) "big" else "small"
<Any 타입, 타입 체크(is)>
- 자바의 Object와 비슷한 개념으로 코틀린에는 Any가 있다
- 코틀린은 우너시타입(Primitive types)에 대해서도 Any가 가능한다
- 타입 체크
- A is 타입
val t : Any = 10
println((t is Int)) //true
<타입 변환(as)>
- 타입 A as 타입B -> A타입을 B타입으로 바꾼다(A, B는 서로 상속관계)
fun main(){
fun test(obj : Any){
if(obj is Int) println("obj is Int")
if(obj is String) println("obj is String")
println("print obj as string > ${(obj as? String ? : "")}"})
}
test(1)
// obj is Int
// print obj as string > ""
test("string")
//obj is String
//print obj as string > strings
<문자열(String)>
- 자바에서 문자열 중간에 변수 값을 추가하고 싶으면 '+' 연산자를 사용했지만,
코틀린은 문자열 내부에 변수(심지어 expression(수식)까지도)를 넣을 수 있다
- 3중 따옴표를 쓰는 문자열 -> ' """ '
- 어떤 문자든 이스케이프 없이 그대로 쓸 수 있다
- 여러 행의 문자열도 가능하다
var version = "1.3.50"
val javaStyle = "Hello, Kotlin" + version + "!" -(1)
val kotlinStyle = "Hello, Kotlin ${version}!" -(2)
-> (1), (2)는 동일
val num = 10
println("val num is equal to 10 : ${num==10}")
println("""\$""") -> \$
println("\$") -> $
<비교 연산(== or ===)>
- 자바와 달리 '객체 내용' 비교에 '==' 연산자를 사용하고, '객체 레퍼런스(reference) 비교'에는 '==='연산자를 사용한다
- 자바에서는 객체 내용 비교에 equals()를 사용
- 문자열 비교도 당연히 '=='연산자를 쓸 수 있다
data class MyClass(val a:Int, val b:String)
//data class auto genderates equals/HashCode/toString/copy
fun main(){
val str1 = "Hello, Kotlin"
var str2 = "Hello, Kotlin"
val class1 = MyClass(10, "class1")
val class2 = MyClass(10, "class1")
val class3 = MyClass(20, "class1")
//3개의 객체들의 reference는 다르다
println(str1==str2) //true(문자열 비교)
println(class1==class2) //true(문자열 비교)
println(class==class3) //false(문자열 비교)
println(class1===class2) //false(reference가 다르기 때문에)
<배열(Array)>
- 배열은 arrayOf(), arrayOfNulls(), emptyArray()로 생성
fun main(){
val intArrays = arrayOf(1,2,3,4,5) //형 변환(Int)을 하지 않아도 된다
val strArrays = arrayOfNulls<String>(5) //5개짜리의 배열(타입 지정 - String)
val dbArrays = emptyArray<Double>() //비어있는 배열에 대한 타입지정 - Double
println(intArrays[0]) //배열 인덱스에 해당하는 값
intArrays[0] = 10 //val로 정의된 배열이지만, 배열 내의 값은 변경이 가능하다
println(intArrays[0])
for(s in strArrays){
print("$s, ")
}
println("")
println(dbArrays.size) //배열의 크기
}
<for and Iteration>
- for문은 iterator와 같이 사용하는 것이 편리
- 특정 범위의 iterator를 만들기 위해 Range를 사용할 수 있다
- Range는 시작과 끝 숫자(또는 문자)를 ...로 연결
- ex) 1..10, 'a'..'z'('.' - 2개)
func main(){
val array = arrayOf("Hello", "This", "is", "Kotlin")
for(a in array) //interator of array
print("$a ")
println("") //Hello This is Kotlin
for((idx, a) in array.withIndex()) //with index(값뿐만 아니라 인덱스까지)
print("($idx, $a)")
println("") //(0, Hello) (1, This) (2, is) (3, Kotlin)
for(i in 1..10) //with range
print("$i")
println("") //1 2 3 4 5 6 7 8 9 10
for(i in 1..10 step 2) //with range and step
print("$i")
println("") // 1 3 5 7 9(2씩 점프)
(10 downTo 1).forEach{ //with lambda(downTo 거꾸로)
print("$it") //it은 default인자
}
println("") //10 9 8 7 6 5 4 3 2 1
//when we use 'while'
var i = 0
while(i<10){
i++
print("$i")
}
println("") //1 2 3 4 5 6 7 8 9 10
}
<Function>
- fun 함수이름(매개변수 리스트) : 리턴타입{함수 정의}
- fun 함수이름(매개변수 리스트) = expression //함수의 내용을 한 줄짜리로 표현 가능할 떄
- 함수 내용이 expression인 경우, 리턴타입이 추정 가능하므로 생략된다
- Default argument 지원 - 인자를 주지 않았을 때 사용
- Named argument 지원 - 인자를 줄 때 매개변수 이름과 값을 줄 때 사용
fun myFunc(arg1: Int, arg2: String = "default", arg3: Int = 10)
{
println("$arg1, $arg2, $arg3")
}
fun sumFunc(a: Int, b: Int) = a+b
fun main(){
myFun(1, "hello", 5) //1, hello, 5
myFun(arg1=2) //2, default, 10
myFunc(2, arg3=5) //2, default, 5
//local function
fun localFunc(a: Int) : Int{
return a + 10
}
println(sumFunc(1,2)) //3
println(localFunc(10) //20
}
<Funciton - Lambda
- 람다(lambda) 함수는 익명 함수
- 람다 함수는 {인자리스트 -> 함수 내용}과 같은 형식으로 작성한다
- 인자가 한 개이고 타입이 생략 가능한 경우 디폴트 인자를 써서 인자리스트 부분을 생략 가능
- 람다함수는 따로 return을 따로 쓰지 않기 때문에 expression형태(x+y)로 표현한다
val sum = {x: Int, y: Int -> x+y} //람다함수 정의
println(sum(10, 20))
fun lambdaTest(a: (Int) -> Int) : Int{ //(int)라는 함수로 되어있는 인자 하나를 받아서 Int를 리턴하는 타입
return a(10)
}
lambdaTest({x -> x+10}) //즉, {x: Int -> x+10}
lambdaTest({it + 10}) //인자 타입 생략 가능, 인자가 1개뿐이므로 인자리스트 생략
lambdaTest{it + 10} //람다가 함수의 유일한 인자이면, 함수의 괄호를 생략할 수 있다(lambdaTest함수의 인자{it+10}로 주어진다)
<Function - Lambda(예제)>
data class MyClass(val a: Int, val b: String)
fun lambdaTest(a: (Int) -> Int) : Int{
}
fun main(){
val sum = {x: Int, y: Int -> x+y}
println(sum(10, 20))
val array = arrayOf(MyClass(10, "class1"), MyClass(20, "class2"), MyClass(30, "class3"))
println(array.filter({c: MyClass -> c.a < 15})) //[MyClass(a=10, b=class2)]
//filter는 함수를 인자로 받아 각 원소의 경우에 함수의 조건에 따라 실행을하고 True 값만 쓴다
//c는 MyClass를 받아서 MyClass안의 a라는 속성의 조건이 15보다 작은 값
//람다가 함수의 인자의 마지막으로 사용되면 함수의 괄호 밖으로 뺄 수 있다
array.filter(){c: MyClass -> c.a < 15}
//람다가 함수의 유일한 인자이면, 함수의 괄호를 생략할 수 있다
array.filter{c: MyClass -> c.a <15}
//인자 타입 생략 가능한 경우
array.filter {it.a < 15}
//디폴트 매개변수 이름으로 it을 사용할 수 있고, 일반적으로 많이 사용하는 형태이다
print(lambdaTest{it + 10})
val title = "Num:"
val list = ListOf(1,2,3,4)
list.forEach{println("$title $it)} //access title in outside of the lambda
}
<Function - Lambda(예제2)>
- 안드로이드 프로그래밍에서 흔히 사용되는 예
1- val btn = findViewById<Button>(R.id.button1)
2- btn.setOnclickListener{
3- Toast.makeText(this. R.string.button_clicked_msg, Toast.LENGTH_SHORT).show()
}
- 3번줄은 2로 등록을 해놓고 3번줄이 호출이되는 건 버튼이 눌릴 때 호출이 된다
<When>
- switch문과 비슷하지만 케이스마다 타입이 달라도 되며 expression으로 사용할 수 있다
fun test(arg : Any){
when(arg){
10 -> println("10")
in 0..9 -> println("0<x<=9")
is String -> println("Hello, $arg") -> 'is' type checking하는 것
!in 0..100 -> println("x < 0 and x>100")
else -> {
println("unknown")
}
}
}
<Null Safety>
- Null이 가능한 타입과 불가능한 타입 구분
- 타입 이름 뒤에 '?'을 붙이면 Nullable 타입
import java.lang.NullPointerException
fun testNull(arg : String?) //String? -> Null인 값이 들어올 수 있다
{
println(arg?.uppercase()) //arg? return null if arg is null
println(arg?.uppercase()? : "-")
//Elvis(?:) return right operand("-") if left operand is null
}
fun main(){
var nullable: String? = null
var nonNullable: String = "nonNullable"
//nonNullable = null // compile Error
testNull(nonNullable) //NONNULLABLE NONNULLABLE
testNull(nullable) //null -
//nullalble.uppercase() //Compile Error
nullable?.uppercase() //return null because nullable is null
try{
nullable!!.uppercase() //cause Exception!
}catch(e : NullPointerException){
println("NullPointerException")
}
}
<Exception>
- try, catch문은 자바와 거의 같으며, try, catch문을 expression을 사용할 수 있다
- 코틀린은 자바와 달리 모든 예외를 catch하게 강제하지 않느다
fun main(){
val x = try{
parseInt("10")
}catch(e : NFE) {
0
}
println(x) //정상적으로 parsing되면 x에 10이 들어가고 오류가 나면 0이 들어간다
}
<Collections>
- Array(배열의 크기는 변경할 수 없지만 값은 변경가능), List, Set, Map(immutable : 내용 변경 불가)
- ArrayList(배열의 크기와 값을 변경할 수 있다), MutableList, MutableSet, MutableMap(mutable)
val array = arrayOf(1,2,3)
val arrayList = array.toMutableList() //arrayListof(1,2,3)
val list = listOf(1,2,3)
val mutableList = list.toMutableList() //mutablsListOf(1,2,3)
val set = setOf(1,2,3,3)
val mutableSet = set.toMutableSet() //mutableSetOf(1,2,3,3)
val map = mapOf("one" to 1, "two" to 2, "three" to 3)
val mutableMap = map.toMutableMap() //mutableMapOf("one" to 1, "two" to 2, "three" to 3)
<Collections에 대한 iteration>
- 빈 Collection 생성, iteration
fun main(){
val e_array = arrayListOf<String>()
val e_list = mutableListOf<Int>()
val e_set = mutableSetOf<Int>()
val e_map = mutableMapOf<String, Int>()
e_array.add("1")
e_list.add(1)
e_set.add(1)
e_map["one"] = 1
for(a in e_array) print("$a")
for(a in e_list) print("$a")
for(a in e_set) print("$a")
for((k, v) in e_map) print("$k - $v")
}
'안드로이드 프로그래밍' 카테고리의 다른 글
안드로이드 레이아웃(LayOut) (0) | 2023.03.30 |
---|---|
코틀린FP (0) | 2023.03.23 |
코틀린OPP (0) | 2023.03.18 |