코틀린 기초

Soomin Kim·2023년 2월 6일
0

Android

목록 보기
3/14

컬렉션

같은 데이터 타입 또는 다른 데이터 타입의 묶음

코틀린에서의 컬렉션

  • 특정 데이터 타입의 요소를 저장하는 클래스
  • 다른 타입의 요소를 저장하는 클래스
IntArray, BooleanArray, DoubleArray, ByteArray..
arrayOf<String>, arrayOf<Fruit> array(1,2,"John",0.3)

Immutable Collections
List - listOf
arrayOf랑 비슷하지만 더 많은 연산 메서드 도우미가 있음.
Set-setOf

  • 중복되는 값이 없게 하는 클래스
    Map - mapOf
  • key - value 한쌍으로 정리

Mutable Collections

  • Mutable List : ArrayList, arrayListOf, mutableListOf
  • Mutable Set: mutableSetOf, hashSetOf
  • Mutable Map: HashMap, hashMapOf, mutableMapOf

Array

package eu.tutorials.kotlinbasics

fun main() {
    val numbers: IntArray = intArrayOf(1, 2, 3)

    // 타입 추론
    val numbers2 = intArrayOf(1,2,3)

    // 더 간단한 방법
    val numbers3 = arrayOf(1,2,3)
    // Array의 주소로 저장된 위치가 출력됨
    println(numbers)
    // array 값 출력
    println("initial values: ${numbers3.contentToString()}")

    for(i in numbers3){
        // 2를 더하는건 오버라이딩 한게 아니라 단지 디스플레이만 한것
        print("${i+2} ")
    }
    print("\n")

    // 인덱스로 접근, 1출력
    println(numbers3[0])

    // 값 변경
    numbers3[0] = 200
    numbers3[1] = 100
    println("initial values: ${numbers3.contentToString()}")

    // 배열은 고정된 크기이므로, 크기를 키우거나 새로운 인덱스를 추가할 수 없음.
    // 에러: ex)numbers[7] = 200

    val fruits = arrayOf(Fruit("Apple", 2.5), Fruit("Grape", 3.6))
    println(fruits.contentToString())
    for (idx in fruits.indices) { // indices는 인덱스를 알려줌
        print("${fruits[idx].name} is in index $idx")
    }

    for (fruit in fruits){
        print(" ${fruit.name}")

    }

    val mix = arrayOf("Sun", "Mon", 1, 2, 3, Fruit("Apple", 2.5))
    print(mix.contentToString())
    //arrayOf의 묘미는 다양한 데이터 타입을 쓸 수 있는것

}

data class Fruit(val name: String, val price: Double){

}

리스트

리스트는 하나의 데이터 타입과 커스템 객체 또는 클래스 모두 저장할 수 있음
새로운 값이 추가되면서 크기도 바뀔 수 있음.
Mutable, Immutable 타입이 있음.

fun main(){
    val months = listOf("Jan", "Feb", "Mar")
    val anyTypes = listOf(1, 2, 3, true, false, "String")

    println(anyTypes.size)
    println(months[1])
    for (month in months){
        println(month)
    }

    // Immutable

    // Mutable로 변경
    val additionalMonths = months.toMutableList()
    val newMonths = arrayOf("April", "May", "June")
    additionalMonths.addAll(newMonths)
    additionalMonths.add("July")
    val addMonths = listOf("August", "Sep", "Oct", "Nov", "Dec")
    additionalMonths.addAll(addMonths)
    //println(additionalMonths)

    // Mutable 리스트 생성
    val days=  mutableListOf<String>("Mon", "Tue", "Wed")
    days.add("Thus")
    // val 오버라이딩
    days[2] = "Sun"
    days.removeAt(0)
    // 다 삭제
    days.removeAll(days)
    // 특정 리스트 삭제
    val removeList = mutableListOf<String>("Mon", "Thus")
    days.removeAll(removeList)

    print(days)
}

Set

중복되는 데이터를 삭제하는 컬렉션
Unordered

package eu.tutorials.kotlinbasics

fun main(){
    val fruits = setOf("Orange", "Apple", "Grape", "Apple", "Mango")
    // 중복으르 제거해주기 때문에, Apple이 삭제되고 사이즈는 4가 됨.
    //print(fruits.size)
    // 알파벳 순서로 정렬
    fruits.toSortedSet()
    val newFruits = fruits.toMutableList()
    newFruits.add("Water Melon")
    print(newFruits)
    print(newFruits.elementAt(4))


    // Map: key-value로 저장, key는 유니크하고, value 하나 가질 수 있음. value가 객체도 올 수 있음
    val daysOfTheWeek = mapOf(1 to "Monday", 2 to "Tuesday", 3 to "Wednesday")
    // 2 는 인덱스가 아니라, key
    print(daysOfTheWeek[2])
    for (key in daysOfTheWeek.keys) {
        print("$key is to ${daysOfTheWeek[key]}")
    }

    val fruitsMap = mapOf("Favorite" to Fruity("Grape", 2.5), "Ok" to Fruity("Apple", 1.0 ))

    // Mutable List로 변경
    val newDayOfWeek = daysOfTheWeek.toMutableMap()
    newDayOfWeek[4] = "Thursday"
    newDayOfWeek[5] = "Friday"
    print("\n")
    // key에 의해 정렬됨
    print(newDayOfWeek.toSortedMap())

}

data class Fruity(val name: String, val price: Double)

ArrayLists

동적 배열을 생성하는 것. 배열은 컬렉션의 일종이니, 컬렉션 사용할 것.
왜 ArrayList를 사용할까?
동적 배열을 사용하기 때문에, 필요에 따라 크기가 증가 또는 감소할 수 있음.
read용 write용 둘다 가능

삽입 시퀀스 순서를 따름.
동기화되지 않아, 중복된 요소를 가질 수 있음.

Constructor of ArrayList

// Empty ArrayList
ArrayList<E>()
// 특정 Capacity
ArrayList(capacity: Int)
//채워질 컬렉션 지정
ArrayList(elements: Collection<E>)

// Add
open fun add(element: E): Boolean
// Clear
open fun clear()
// return at specific index
open fun get(index: Int): E
// remove
open fun remove(element: E): Boolean

// val -> <String>은 바꿀수없는데 요소들은 바꿀수있음
val arrayList = ArrayList<String>()

//ex
val arrayList: ArrayList<String> = ArrayList<String>(5)
val list: MutableList<String> = mutableListOf<String>()
list.add("One")
list.add("Two")
arrayList.addAll(list)

//iterator는 array 그 자체
val itr = arrayList.iterator()

while (itr.hasNex()){
	println(itr.next())
}

println(arrayList.get(1)) // "Two"

Lambda Expressions

람다 or 람다 표현식: 이름이 없는 함수
람다 표현식과 익명의 함수는 '함수 리터럴'. 선언되지 않고 바로 표현식으로 전달된 함수
람다는 중괄호로 정의되고, 매개변수와 함수 바디로 변수를 받음. 매개변수로 변수를 받아도 되지만 꼭 그런건 아님

  • 함수 바디는 변수 뒤에 화살표와 연산자와 함께 쓰임. (람다 연산자. ->)
    []로 둘러싸임

고급 기능을 가진 코드를 간결하고 짧게 정리해줌

// Int를 리턴받는다고 정의했으니까( -> Int) , Int를 리턴해야됨 ( Int = .. )
val sum: (Int, Int) -> Int = {a: Int, b: Int -> a+b}
println(sum(10,5))

// 더 짧게
val sum = {a: Int, b: Int -> println(a+b)}
sum(10,5)

Visibility Modifiers (접근 제한자)

정의: 코틀린에서 클래스, 인터페이스, 메서드, 프로퍼티의 사용을 제한하는 데 사용하는 키워드

  • 클래스 헤더나 메서드 바디 등 많은 데서 쓰임
  • 4가지 분류 (public, private, protected, internal)

public modifier

  • 프로젝트 어디서든 접근 가능
  • default modifier.
  • 모든 public 선언은 파일 맨 위에 위치
public class Example {}
class Demo {
}
public fun hello()
fun demo()
public val x = 5
val y = 10

Private Modifier

  • 프로퍼티, 필드 등이 선언된 블록에서만 요소에 접근할 수 있게 해줌
  • 스코브 밖으로의 접근을 막음
  • private 패키지는 그 특정 파일 내에서만 접근 가능
// class Example은 파일 안에서 접근 가능
private class Example {
	// val x, fun doSomething()은 class Example 안에서만 접근 가능
    // Example 객체를 만들어도 접근 불가
	private val x = 1
    private doSomething(){
    }
}

Internal Modifier

  • 시행된 모듈 안에서만 필드가 보이게 함.
  • 모든 필드는 internal로 선언되고, 이는 시행된 모듈 안에서만 접근 가능
internal class Exmaple{
	internal val x = 5
    internal fun getValue() {
    }
}
internal val y = 20
}

Open keyword

  • 코틀린에서 모든 클래스는 기본값으로 최종이지, 기본값으로 상속받을 수 없음.
  • 자바에서는 반대. 모든 클래스가 상속할 수 있기 때문에 클래스를 최종으로 명시해줘야됨.
  • 상속 사용하려면 클래스를 키워드 open 써줘야됨

Protected Modifier

  • 클래스 또는 인터페이스의 Protected 제한자는 그 안의 클래스 또는 서브 클래스에 보이게 해줌.
  • 서브 클래스 안의 오버라이딩한 Protected 선언은 변경을 명시하지 않는 한 보호됨.
  • Protected 제한자는 최상위에 선언될 수 없음. 즉 패키지는 보호받을 수 없음. 패키지에 다른 접근 제한자를 사용해야 함.
open class Base{
	protected val i = 10
}
class Derived : Base() {
	// Base로 상속받았으니까, 변수 i를 쓸 수 있는 것.
	fun getValue(): Int {
    	return i
    }
}

// 오버라이딩 protected 타입을 오버라이딩한 예시. open protected
open class Base{
	open protected val i = 5
}

class Another : Base() {
	fun getValue() : Int {
    	return i
        }
    // 이렇게 뭔가 입력하고 싶으면 키워드 open을 추가해야 됨
    override val i = 10
}
open class Base() {
	val a = 1 // public by default
	private var b = 2  // private to Base class
	protected open val c = 3 // visible to the Base and the Derived class
	internal val d = 4 // visible inside the same module
	protected fun e() {} // visible to the Base and the Derived class
}

class Derived: Base() {
	// a,c,d,e() of the Base class are visible
    // b is not visible
    override val c = 9 // b.c c is protected and open
}

fun main(args: Array<String>) {
	// base.a and base.d are visible
    // base.b and base.c and base.e() are not visible (c, e()는 protected 이므로, 같은 클래스 또는 파생된 클래스에서만 사용 가능)
	val base = Base()
    // derived.c is not visible. 오버라이딩 했지만 여전히 protected이기 때문에
    val derived = Derived()
}

Nested class and Inner class (중첩, 내부 클래스)

중첩 클래스: 다른 클래스 안에 생성된 클래스

  • 자동으로 정적 -> 데이터 멤버와 멤버 함수는 클래스 객체 생성 없이 접근할 수 있음.
  • 일방적인 관계. 외부 클래스의 데이터멤버에 접근할 수 없음.
class OuterClass{
	//outer class code
    private var name: String = "Mr X"
    class NestedClass {
    	// nested class code
        var description: String = "code inside nested class"
        private var id: Int = 101
        fun foo() {
        	// print("name is ${name}") // cannot access the outer class member
       	println("ID is ${id}")
        }
    }
}

fun main(args: Array<String>) {
	// nested class must be initialized
    println(OuterClass.NestedClass().description) // accessing property
    var obj = OuterClass.NestedClass() // object creation
    obj.foo() // access member function
}
// Output: code insde nested class
			Id is 101

내부 클래스: inner 키워드와 함께 다른 클래스 안에 생성된 클래스

  • inner라고 표시된 중첩 클래스
  • 인터페이스 안에 또는 내부 중첩 클래스가 아닌 곳에 선언될 수 없음.
  • private이여도 외부 클래스 멤버에 접근 가능
  • 외부 클래스 객체의 참조를 저장
class OuterClass{
	//outer class code
    private var name: String = "Mr X"
    inner class InnerClass {
    	// nested class code
        var description: String = "code inside nested class"
        private var id: Int = 101
        fun foo() {
        	print("name is ${name}") // access the private outer class member
       	println("ID is ${id}")
        }
    }
}

fun main(args: Array<String>) {
    println(OuterClass.NestedClass().description) // accessing property
    var obj = OuterClass.NestedClass() // object creation
    obj.foo() // access member function
}
// Output: code insde nested class
			Name is Mr X
			Id is 101

Unsafe and Safe Cast 연산자

Unsafe cast operator: as

  • 어떤 때는 변수를 캐스트하지 못하고 예외처리 되는데, 이걸 Unsafe Cast라 부름
  • 불안전한 캐스트는 infix 연산자 as에 의해 실행됨.

EX) Nullable String(String?)은 Non-nullable string(String)으로 cast 될 수 고, 이는 예외 에러를 발생시킴.

fun main(args: Array<String>){
	val obj:Any? = null
    val str: String = obj as String
    println(str)
}

// Output: Exception in thread "main" kotlin. TypeCastException: null cannot be cast to non-nullable type.

Any 타입의 정수 값을 String 타입으로 캐스트 하는 것은 ClassCastException 발생

val obj: Any = 123
val str: String = obj as String

Nullable for Casting to work

  • 둘다 Nullable
fun main(args: Array<String>){
	val obj: Any? = "String unsafe cast"
    val str: String? = obj as String? // works
    println(str)
}

Safe cast operator: as?

  • 안전하게 타입 캐스트.
  • casting이 불가능하면 ClassCastExceptioin을 발생시키는 대신, null을 리턴함.
fun main(args: Array<String>){
	val location: Any = "Kotlin"
    val safeString: String? = location as? String
    val safeInt: Int? = location as? Int
    println(safeString) // Kotlin
    println(safeInt) // null

Exception Handling

  • 프로그램의 런타임 문제 처리하는 것.
  • Exception: 프로그램 종료를 초래하는 프로그램의 런타임 문제
  • ex. running out of memory, array out of bound, condition like divided by zero.
  • Exception handling을 통해 의도한대로 되지 않더라도 프로그램이 고장나지 않고 흐름을 유지하며 작동

Throwable Class
throw MyExceptioin ("this throws an exception")
4개 키워드

  • try
  • catch
  • finally
  • throw

try

  • 예외를 발생시킬 수 있는 구문 세트를 포함.
  • 뒤에 catch or finally or 둘다가 뒤따라 와야됨.

catch

  • try에서 던진 예외를 잡는 것. try 블록에 예외가 있으면 잡고, 없으면 실행 안됨.

finally

  • 예외가 처리되든지 말든지 실행됨. 중요한 코드 구문을 실행하는데 사용. ex. 버퍼를 닫는 것

throw

  • 명료하게 예외를 던져줌.
  • 오류를 일으키기 위해 입력하는 것.
  • 어디서 오류가 일어나는지 테스트할 때 매우 유용. 고유의 예외나 throw를 만들어서 적은 것을 알아냄

Unchecked Exception

  • 코드 실수 때문에 던져지는 예외
  • 이 예외 타입은 RuntimeException 클래스를 확장
  • 앱이 실행되는 런타임에 확인됨.(실행 버튼 눌러 앱을 컴파일 할 때 말고)
    ex)
  • ArithmeticException: 숫자를 0으로 나눴을 때
  • ArrayIndexOutOfBoundException: 잘못된 인덱스로 배열 접근할 때
  • SecurityException: 보안위반. ex) GPS 사용하려는데 GPS기능 사용할 권리 주지 않아 접근 불가능
  • NullPointerException: null 객체에 메서드나 프로퍼티 호출. null로 비어있는 객체에 접근하려 했을 때 나는 오류

Checked Exception

  • 컴파일될때 확인되는 예외
  • Throwable 클래스를 확장
    EX)
  • IO Exception: 입출력 오류
  • SQLException etc...

try catch

try{
	// code that may throw the exception
} catch(e: SomeExceptioin){
	.. code that handles the exception
}

Example without exception handling (예외처리 없는 경우)

val str = getNumber("10") // The variable "str" is getting the int value of "10"
println(str)
fun getNumber(str: String): Int {
	return try{
    	Integer.parseInt(str)
        }
        catch(e: ArithmeticException){
        	0
        }
    }
//Output: 10

Example with exception handling

val str = getNumber("10.5") // The variable "str" is getting the int value of "10.5"
println(str)
fun getNumber(str: String): Int {
	return try{
    	Integer.parseInt(str)
        }
        catch(e: ArithmeticException){
        	0
        }
    }
//Output: 0 (Catch Block)

Multiple catch Blocks

  • 다양한 연산 오류가 있을 때, 즉 try 블록에서 다양한 에러가 날 때 사용
fun main(args: Array<String>) {
	try{
		val a = IntArray(5)
      	a[5] = 10 / 0
    } catch(e: ArithmeticException) {
    	println("arithmetic exception catch"
    } catch(e: ArrayIndexOutOfBoundsException) {
    	println("array index outofbounds exception")
    } catch(e: Exception) {
    	println("parent exception class")
    }
    println("code after try catch")
 } 
 //Output: arithmetic exception catch
 //			code after try catch

Nested try-catch block

  • 코드블록이 예외를 발생시키거나 블록 내의 다른 코드가 예외를 발생시킬 때 필요
  • 예외의 시작
try{
// code block
	try {
    // code block
    }catch(e: SomeException){
    // exception
    }
} catch(e: SomeException){
    // exception
}
    

Finally Block

  • 예외가 처리되든 말든 실행되는 블록
fun main(args: Array<String>) {
	try{
		val data = 10 / 5
      	println(data)
    } catch(e: NullPointerException) {
    	println(e)
    } finally{
    	println("finally block always executes")
    }
    println("below code..")
 } 
 // output
 // 2
 // finally block always executes
 // below code..
throw keyword
  • 분명한 예외를 던지는데 사용
  • 커스텀 예외를 던지는 데도 사용됨 ex) 예외 객체를 던지는데 사용
  • 코드가 올바르게 됐는지 확인하고 싶을 때 유용
  • 코드를 고장 낼 오류를 던져서 문제 발생 원인을 이해하는 것
    throw SomeException()
fun main(args: Array<String>) {
	validate(15)
    println("code after validation check..")
}

fun validate(age: Int) {
	if (age < 18)
    	throw ArithmeticException("Under age")
    else
    	println("Eligible for drive")
}
// Output: Exception in thread "main" java.lang.ArithmeticException: under age
profile
개발자지망생

0개의 댓글