Android Kotlin 심화
업데이트:
카테고리: Android
/태그: Kotlin, Try-Catch문, 람다식, 접근제한자, 컬렉션
컬렉션
코틀린에는 2가지 컬렉션이 존재
- 특정 데이터 타입의 요소를 저장하는 클래스
Array
: 같은 타입의 요소를 저장함
- 다른 타입의 요소를 저장하는 클래스
arrayOf
: 모든 타입을 저장함
- 변경 불가능한 클래스
listOf
:arrayOf
와 비슷하지만 더 많은 메서드가 있고 크기를 쉽게 조정가능setOf
: 중복된 값이 없게 함mapOf
: key-value 페어로 이루어짐
배열
미리 타입값을 지정하지 않아도 타입 추론으로 코틀린 내부에서 자동으로 타입이 지정된다.
배열은 그냥 출력하게 되면 주소값이 나오게 되므로 contentToString()
을 통해 배열의 값을 문자로 변환 후 출력해야 한다.
contentToString
은 이름만 알려주므로 자세하게 알고 싶다면 index로 접근해야한다.
indicies
는 객체의 인덱스 번호를 알려주는 메서드이다.
fun main(){
// val numbers:IntArray = intArrayOf(1, 2, 3, 4, 5, 6)
val numbers = arrayOf(1, 2, 3, 4, 5, 6)
print(numbers.contentToString())
// 배열 값 변경
numbers[0] = 6
numbers[1] = 5
numbers[4] = 2
numbers[5] = 1
// 원하는 데이터 타입으로 배열을 만들 수 있다.
val fruits = arrayOf(Fruit("Apple", 2.5), Fruit("Grape", 3.3))
// 객체 값이 하나씩 fruit에 들어간다.
for (fruit in fruits){
print(" ${fruit.name}")
}
// 전체 fruits의 인덱스 만큼 index가 순서대로 들어간다.
for(index in fruits.indicies){
print("${fruits[index].name} is in index $index")
}
}
data class Fruit(val name: String, val price:Double)
리스트(listOf)
어떠한 데이터 타입도 담을 수 있다.
llstOf
: 변경불가한 리스트, 어떤 데이터든 담을 수 있다.mutableListOf
: 변경가능한 리스트, 들어갈 데이터 타입을 미리 지정해야 한다.fun main() { val months = listOf("Jan", "Feb", "Mar") val anyTypes = listOf(1, 2, 3, true, false, "String") // list는 immutable한 배열이므로 toMutable을 통해 변경 가능하도록 바꾼다. val addtionalMonths = months.toMutablList() val newMonths = arrayOf("Apr", "May", "Jun") additionalMonths.addAll(newMonths) additionalMonths.add("Jul") val days = mutableListOf<String>("Mon", "Tue", "Wed") days.add("Thu") // removeAt : 인덱스를 통해 삭제 days.removeAt(3) // removeAll : 특정 값만 리스트에서 제거 val removeList = mutableListOf<String>("Mon", "Wed") days.removeAll(removeList) }
집합(Set)
중복되는 값을 제거하고 순서가 없다.
setOf
: 변경불가한 집합, 어떤 데이터든 담을 수 있다.mutable SetOf
: 변경가능한 집합, 들어갈 데이터 타입을 미리 지정해야 한다.hashSetOf
:fun main(){ val fruits = setOf("오렌지", "사과", "포도", "사과", "망고") // toSortedSet : 순서대로 정렬한다. print(fruits.toSortedSet()) val newFruits = fruits.toMutableist() newFruits.add("수박") newFruits.add("배") // elementAt : 특정인덱스로 접근 newFruits.elementAt(4) }
맵(map)
- key - value 형태로 데이터를 저장하는 컬렉션
- key는 하나의 값만 저장 가능
to
를 통해서 키와 밸류값을 지정- value는 어떤한 값으로도 가능
생성자
mapOf
: immutable-
mutable mapOf
: mutablefun main(){ val daysofTheWeek = mapOf(1 to "Monday", 2 to "Tuesday", 3 to "Wedsnesday") // key으로 value에 접근할 수 있다. for(key in daysOfTheWeek.keys){ print("$key is to ${daysOfTheWeek[key]}") } val fruitsMap = mapOf("Favorite" to Fruit("Grape", 2.5), "OK" to Fruit("Apple", 1.0)) // mutable한 객체로 변경 val newDaysOfWeek = daysToTheWeek.toMutableMap() newDaysOfWeek[4] = "Thursday" newDaysOfWeek[5] = "Friday" // 정렬 print(newDaysOfWeek.toSortedMap()) } data class Fruit(val name: String, val price: Double)
ArrayLists
- 동적 배열을 만들어준다. 그러므로 필요에 따라 크기가 늘고 줄어들 수 있음
- 읽기 & 쓰기 둘 다 가능
- 삽입 시퀸스 순서를 따름
- 중복 요소 포함 가능
- 하지만 immutable한 배열이기에
val
로 생성
ArrayList 생성자
ArrayList<E>()
: 비어 있는 배열리스트 생성ArrayList(capacity: Int)
: 길이가 정해진 배열리스트 생성ArrayList(elements: Collection<E>)
: 컬랙션 요소로 채울 수 있는 배열리스트 생성
Array List Function
add()
: 특정요소를 컬랙션에 추가clear()
: 모든요소를 제거get()
: 특정 값에 대한 인덱스값의 정보를 반환remove()
: 특정 요소를 제거함, 만약 값이 여러개일 경우 첫번째의 값이 사라짐iterator()
: 배열리스트가 값을 가지고 있는 한 루프를 계속돎
람다식
- 이름이 없는 함수
- 선언되지 않고 곧바로 표현식으로 넘어감
- 화살표(->)와 함께 사용
- 고급 기능을 가진 코드를 간결하고 짧게 정리
- 문법 :
{ variables(매개변수) -> lambda식 }
두 숫자를 더하는 람다함수
// 기존
fun addNumber (a: Int, b: Int){
return a + b
}
// 람다식
val sum: (Int, Int) -> Int = {a: Int, b: Int -> a + b}
val sum = {a: Int, b: Int -> print(a + b)}
접근제한자
- 클래스, 인터페이스, 프로퍼티를 제한하는데 사용
- 클래스 헤더나 메서드 바디 등 여러 곳에서 사용
종류
public
- 프로젝트 어디서든 접근 가능
- 코틀린에서 기본 제한자
- 공개 선언은 파일 위에 선언
private
- 선언된 블록에서만 접근 가능
- 스코프 밖으로의 접근을 막음
- 같은 클래스나 같은 파일 안에서만 사용이 가능
protected
- 그 안의 클래스 또는 서브 클래스에 보이도록 함
- 오버라이딩으로 변경하지 않는 이상 남아있음
- 최상위에 선언될 수 없음, 즉 패키지는 보호 받을 수 없음
internal
- 자바에는 없는 코틀린만의 기능
- 시행된 파일안에서만 필드가 보이드록 함
- 모든 필드는 internal 필드로 선언됨
open
- 코틀린에서 모든 클래스는 자동으로 상속받을 수 없음
- 상속을 사용하기 위해서는
open
키워드를 사용해야 됨 - 오버라이딩할 변수나 함수 또한 해당 키워드를 사용해야 됨
중첩클래스 & 내부클래스
중접 클래스
- 다른 클래스 안에 생성되어 자동으로 정적임
- 객체를 만들지 않고, 함수와 변수를 사용 가능
- 객체에서 프로퍼티처럼 사용이 가능함
- 중첩 클래스는 외부 클래스의 데이터에 접근 불가
class OuterClass{ private var name: String = "Mr X" class NestedClass{ var description: String = "code inside nested class" private var id: Int = 101 fun foo() { // Error : 중첩 클래스에서 외부 클래스의 데이터는 접근 불가 print("name is ${name}") print("Id is ${Id}") } } } fun main(args:Array<String>){ // 객체를 따로 만들지 않아도 중첩 클래스 내부에 접근 가능 println(OuterClass.NextedClass().description) // code insie.. var obj = OuterClass.NestedClass() obj.foo() // Id is 101 }
내부 클래스
- 키워드
inner
로 다른 클래스 안에 만들어진 클래스 - 중첩 클래스와 유사하나 큰 차이점은 인터페이스 안 또는 내부 중첩 클래스가 아닌 곳에는 선언될 수 없음
- private이더라도 외부 클래스 데이터에 접근이 가능함
- 외부 클래스 객체의 참조를 저장
- 객체 생성시
()
를 통해 OuterClass에 접근해야 됨class OuterClass{ private var name: String = "Mr X" inner class InnerClass{ var description: String = "code inside nested class" private var id: Int = 101 fun foo() { print("name is ${name}") print("Id is ${Id}") } } } fun main(args:Array<String>){ // 생성시 OuterClass도 ()로 접근해야 됨 println(OuterClass().InnerClass().description) // code insie.. var obj = OuterClass().NestedClass() obj.foo() // name is }
UnSafe & Safe Cast
Cast
: 특정 타입을 다른 타입으로 바꾸는 것
Unsafe Cast : as
- 어떤 떄는 변수를 캐스트하지 못하고 예외 처리됨
- infix 연산자 as에 의해 실행됨
fun main(args: Array<String>){ val obj: Any? = null val str: String = obj as String // Error : null은 String으로 캐스트 불가 println(str) val obj: Any = 123 // ClassCastException val str: String = obj as String }
⇒ 캐스팅되기 위해서는 변수와 소스는 항상 Nullable이어야 한다.
Safe Cast : as?
- 한 타입으로 안전하게 캐스트하도록 도움
- 캐스팅할 수 없을 때 예외처리가 아닌 null값을 반환
fun main(args: Array<String>){ val location: Any = "Kotlin" val safeString: String? = locatoin as? String val safeInt: Int? = location as? Int println(safeString) // Kotlin println(safeInt) // null }
예외처리 - Try-Catch문
Exception
- 프로그램의 런타임 문제
- 프로그램 종료를 초래함
- 저장공간 부족
- out fo bound 배열
- divide 0
- 프로그램 실행에서 해당 문제를 처리하기 위해 Exception handling을 사용
- 런타임 문제를 처리하고 프로그램의 원할한 작동하도록 함
Throwable Class
- 예외를 던지게 함
- 총 4개의 클래스가 존재
try
- 예외를 발생시킬수 있는 구문
catch
,finally
중 하나는 반드시 뒤에 작성해야함
catch
try
에서 던져진 예외를 잡음- 예외가 없다면 실행되지 않음
finally
- 예외가 처리되든 말든 실행
- 중요한 코드 구문을 실행하는 데 사용
throw
- 명료하게 예외를 던짐
- 오류를 일으키기 위해 입력
- 오류가 어디서 나는지 테스트할 때 유용
Try-Catch 구문
// 예외 처리가 없는 구문
val str = getNumber("10")
println(str) // 10
fun getNumber(strL String): Int {
return try {
Integer.parseInt(str)
} catch(e:ArithmeticExption){
0
}
}
// 예외 처리가 있는 구문
val str = getNumber("10.5")
println(str) // 0
fun getNumber(strL String): Int {
return try {
Integer.parseInt(str)
} catch(e:ArithmeticExption){
0
}
}
Multiple catch Block
catch를 여러개 사용하여 다양한 오류에 대처할 수 있다.
fun main(args: Array<String>){
try{
val a = IntArray(5)
a[5] = 10/0
} catch(e: ArithmeticException){
...
} catch(e: ArrayIndexOutOfBoundExceptions){
...
// Exception은 모든 예외처리에 반응하게 된다.
} catch(e: Exception){
...
}
}
Nested try-catch Block
코드 블록이 예외를 발생하거나 블록 내의 다른 코드가 예외를 발생시킬 때 사용
try {
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..")
}
throw keyword
fun main(agrs:Array<String>){
validata(15)
println("code after validation check...")
}
fun validata(age: Int){
if (age < 18)
// 정확한 오류를 던져주기 위해 사용
throw ArithmeticException("under age")
else
println('eligible for drive')
}
Unchecked Exception
- 코드 실수 때문에 던져지는 예외
- 코드가 어떤 이유로 실행되지 않음
- 앱이 실행되는 런타임에 확인
종류
ArithmeticException
: 숫자를 0으로 나눈 경우ArrayIndexOutOfBoundExceptions
: 인덱스 값이 올바르지 않은 경우SecurityException
: 보안 위반, GPS 사용 시 사용할 권리를 주지 않아 접근이 불가한 경우NullPointerException
: null로 비어있는 객체에 접근하려 한 경우
Checked Exception
Throwable
클래스로 확장- 해당 클래스에서 예외 함수를 이용하여 예외 처리를 함
종류
IOException
: 입력 출력 예외SQLException
… 등이 존재