이 글은 이것이 안드로이드다 with 코틀린(개정판)를 참고하여 작성하였습니다.
작성자 : 김용순
개발환경은 Windows, Android Studio입니다.
1. 람다식
람다 표현식(lambda expression) : 이름이 없는 함수, 함수 리터럴(function literal)이다.
선언되지 않고 표현식 자체로 전달된다.
함수를 재사용하지 않을 때 유용하게 사용할 수 있다.
예시
fun addnNumber(a: Int, b: Int): Int {
return a + b
}
위 함수를 아래와 같이 람다 표현식으로 작성할 수 있다.
val sum: (Int, Int) -> Int = { a: Int, b: Int -> a + b }
앞의 부분을 생략해서 더욱 짧게 표현할 수 있다.
val sum = { a: Int, b: Int -> a + b }
2. 접근 제한자
접근 제한자 : 클래스, 인터페이스, 프로퍼티를 제한하는데 사용하는 키워드이다.
public : 프로젝트 어디서든 접근 가능
코틀린에서의 기본 제한자이다(다른 제한자가 명시되지 않으면 자동으로 public이 된다.)
파일 맨 위에 위치한다.
public class Example {
}
public fun hello()
public val x = 5
private : 프로퍼티, 필드 등이 선언된 블록에서만 요소에 접근 가능
스코프 밖으로의 접근을 막는다.
private 패키지는 그 특정 파일 내에서만 접근할 수 있다.
fun main() {
var ex = Example()
ex.x = 2 // private property -> 실행 되지 않는다.
ex.hello() // private method -> 실행 되지 않는다.
}
private class Example {
private var x = 1
private fun hello() {
println("hello")
}
}
open
코틀린에서 모든 클래스는 기본값이 final로 되어있기때문에 상속받을 수 없다. ➡️자바와는 반대이다.
코틀린에서 상속을 사용하려면 클래스를 open으로 만들어야 한다.
protected
안에있는 클래스, 서브 클래스에서만 접근 가능
(private + 서브 클래스 에서 까지 사용 가능하다.)
서브 클래스 안의 오버라이딩한 protected 선언은 변경을 명시하지 않는 protected이다.
Top-level에 선언될 수 없다(패키지는 protected가 될 수 없다.)
open class Base {
open protected val i = 0 // Derived에서 override하기 위해 open으로 지정해야한다.
}
class Derived: Base() {
fun getValue(): Int {
return i
}
override val i = 10 // Base 클래스의 i를 override한다.
}
internal
자바에 없는 코틀린만의 기능
시행된 모듈 안에서만 필드가 보이게 한다.
코틀린에서 제한하는 모듈의 범위
- IntelliJ IDEA 모듈
- Maven / Gradle 프로젝트
- 하나의 Ant 태스크 내에서 함께 컴파일되는 파일들
*프로젝트 > 모듈 > 패키지
internal class Example {
internal val x = 5
internal fun hell() {
}
}
internal val y = 10
접근 제한자 사용 예시
open class Base() {
var 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, and e() of the Base class are visible
// b is not visible
override val c = 9 // c is protected
}
fun main(args: Array<String>) {
val base = Base()
// base.a and base.d are visible
// base.b, base.c and base.e() are not visible
val derived = Derived()
// derived.c is not visible
}
정리
| 클래스, 인터페이스, 프로퍼티 | 패키지 | |
| private | 해당 클래스, 인터페이스 내부 | 해당 .kt 파일 내부에서만 |
| protected | private + subclass 까지 | X |
| internal | 같은 모듈 내부 | 같은 모듈 내부 |
| public | 어디서든 사용 가능 | 어디서든 사용 가능 |
3. 중첩, 내부 클래스
중첩 클래스(Nested class) : 다른 클래스 안에 생성된 클래스
default 값 static이다
➡️ 데이터 멤버와 멤버 함수는 클래스 객체를 생성하지 않고도 접근할 수 있다.
외부 클래스의 데이터 멤버에 접근할 수 없다.
외부 클래스 ➡️ 중첩 클래스 ✅
중첩 클래스 ➡️ 외부 클래스 ❌
class OuterClass {
private var name: String = "Mr X"
class NestedClass {
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 id ${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 inside nested class
// Id is 101
내부 클래스(Inner class) : inner키워드를 사용해 만든 다른 클래스 안의 클래스
inner라고 표시된 중첩 클래스가 내부 클래스이다.
내부 중첩 클래스가 아닌 곳 or 인터페이스 안에 선언될 수 없다.
외부 클래스 멤버가 private라도 접근할 수 있다.
외부 클래스의 객체가 먼저 생성되어야 내부 클래스 객체가 생성될 수 있다.
class OuterClass {
private var name: String = "Mr X"
inner class InnerClass {
var description: String = "code inside inner class"
private var id: Int = 101
fun foo() {
println("name is ${name}") // access the private outer class member
println("Id is ${id}")
}
}
}
fun main(args: Array<String>) {
println(OuterClass().InnerClass().description) // accessing property
var obj = OuterClass().InnerClass() // object creation
obj.foo() // access member function
}
// Output
// code inside inner class
// name is Mr X
// Id is 101
의문점
// 중첩 클래스와 내부 클래스 사용 방법의 차이
OuterClass.NestedClass().description
OuterClass().InnerClass().description
// 내부 클래스는 외부 클래스의 객체를 참조하기 때문일것 같긴하다...
// OuterClass.~~~ : 외부 클래스
// OuterClass().~ : 외부 클래스 객체
// 내부 클래스가 인터페이스 안에 선언될 수 없는것도 이런 이유 때문일 것이다.
// 스터디때 토의해보기
4. Safe Cast, Unsafe Cast 연산자
Cast : 특정 타입의 것을 다른 타입으로 변환하는것
Unsafe cast
변수를 캐스트하지 못하고 예외처리되는 경우
as 연산자 사용한다.
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-null type kotlin.String.
null 값을 String으로 캐스트할 수 없기 때문에 예외처리된다.
따라서 코드를 아래와 같이 고쳐야 한다.
fun main() {
val obj: Any? = null
val str: String? = obj as String?
println(str)
}
Safe cast
as? 연산자를 사용한다.
캐스팅 할 수 없을 때 예외처리 대신에 null 값을 반환한다.
따라서 Unsafe cast 보다 Safe cast를 사용하는것이 더 바람직하다.
fun main() {
val location: Any = "Kotlin"
val safeString: String? = location as? String
val safeInt: Int? = location as? Int
println(safeString)
println(safeInt)
}
// Output
// Kotlin
// null
5. Try-Catch 문으로 예외 처리하기
Exception : 프로그램의 runtime-problem이고, 프로그램 종료를 일으킨다.
- 저장 공간 부족
- 배열 범위를 벗어남
- 0으로 나눔
이런 Exception 문제를 처리하기 위해 예외 처리(Exception handling)를 사용한다.
Throable Class : 예외를 던지게 해 준다.
throw MyException("this throws an exception")
예외 처리에서의 4가지 키워드
- try
예외를 발생시킬 수 있는 구문을 포함한다.
뒤에 catch나 finally가 반드시 있어야 한다.
- catch
try 블록에서 던진 예외를 잡는다.
try 블록에서 예외가 발생하면 catch 블록이 실행된다.
- finally
예외 처리에 상관 없이 실행된다.
주로 중요한 코드 구문을 실행하는데 사용된다.
- throw
예외를 일으키기 위해 사용한다.
오류가 어디서 발생하는지 테스트 할 때 유용하다.
Unchecked Exception
코드 실수에 의해 발생하는 예외
RuntimeException에서 확장된 것이다.
런타임동안 발생한다.
| ArithmeticException | 숫자를 0으로 나눔 |
| ArrayIndexOutOfBoundException | 틀린 인덱스 값으로 배열을 확인 |
| SecurityException | 보안 위반사항 발생 |
| NullPointerException | null 객체에 매서드나 프로퍼티를 호출 |
Checked Exception
컴파일 시간에 발생하는 예외
Throwable 클래스로 확장된다.
- IOException
- SQLException
try-catch
package eu.tutorials.kotlinbasics
fun main() {
val str = getNumber("10.5")
println(str)
}
fun getNumber(str: String): Int {
return try {
Integer.parseInt(str)
} catch (e: ArithmeticException) {
0
}
}
// Output : 0
try 블록에서 여러 예외가 발생할 때를 위해 여러 catch 블록을 사용할 수 있다.
*이때 하위 타입의 예외를 먼저 catch 문으로 선언해야 여러 예외 처리를 수행할 수 있다.
fun main() {
try {
val a = IntArray(5)
a[5] = 10 / 0 // arithmatic, arrayindexoutofbounds exception
} catch (e: ArithmeticException) {
println("arithmetic exception catch")
} catch (e: ArrayIndexOutOfBoundsException) {
println("array index out of bounds exception")
} catch (e: Exception) {
println("parent exception class")
}
println("code after try catch...")
}
// Output
// arithmetic exception catch
// code after try catch...
Nested try block
fun main() {
try {
// code block
try {
// code block
} catch (e: SomeException) {
// exception
}
} catch (e: SomeException) {
// exception
}
}
finally
fun main() {
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...
예외 발생 여부에 상관없이 finally 블록이 실행되었다.
throw
예외를 던지는데 사용된다.
커스텀 예외를 던질 때도 사용된다.
fun main() {
validate(15)
println("code after validation check..")
}
fun validate(age: Int) {
if (age < 18)
throw ArithmeticException("under age")
else
println("eligible for dive")
}
// Output : Exception in thread "main" java.lang.ArithmeticException : under age'GDSC HUFS 4기 > Kotlin Team #2' 카테고리의 다른 글
| [2팀] 코틀린으로 XML, UI를 생성해서 계산기 만들기 (0) | 2022.10.31 |
|---|---|
| [2팀] 코틀린으로 분 단위 계산기 만들기 (0) | 2022.10.26 |
| [2팀] 객체지향 프로그래밍 기초(2) (1) | 2022.10.13 |
| [2팀] 코틀린 객체지향 프로그래밍 기초 (1) (1) | 2022.10.05 |
| [2팀] 코틀린 기초 (0) | 2022.10.03 |