티스토리 뷰
Android
[kotlin] 코틀린을 코틀린답게 (함수,상속과 구현,this,companion object,제네릭,예외,null 연산자)
YEJINEE 2020. 3. 4. 03:02함수
- 값을 반환하지 않는 함수는 Unit타입을 반환하며 생략 가능 (Java의 void와 같음)
class Foo{
fun foo(): Unit{ //반환값이 없는 함수, Unit 생략 가능
//함수 구현
}
fun bar(): Int{ //정수형을 반환하는 함수
return 0
}
}
상속 및 인터페이스 구현
- 코틀린에서는 상속과 구현 상관없이 세미콜론(:) 뒤에 부모 클래스와 인터페이스를 표기
- 클래스를 상속받는 경우 반드시 부모 클래스의 생성자를 호출해야함
- 상속받거나 구현한 함수의 앞에 반드시 override 키워드를 붙혀야함
class MainActivity: AppCompatActivity(), View.OnClickListener{ //부모 클래스의 생성자 호출
//AppCompatActivity 클래스를 상속박아 onCreate()메소드 오버라이딩
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
}
//View.OnclickListener 인터페이스 구현
override fun onClick(v: View) {
}
}
- 코틀린에서는 open 키워드가 붙은 클래스와 함수만 재정의 가능
open class OpenClass { //상속 가능한 클래스
open val openProperty = "open" //상속받은 클래스에서 재정의 가능한 프로퍼티
open fun openFuntion() {} //상속받은 클래스에서 재정의 가능한 함수
}
classFinalClass: OpenClass() { //OpenClass를 상속받는 클래스
override val openProperty = "final" //프로퍼티를 재정의
override fun openFuntion() { println("open funtion") } //함수를 재정의
}
this
- 해당 키워드를 사용한 클래스 자신을 지칭할 때 사용
- 단독 사용시 해당 위치에서 가장 가까운 범위의 클래스를 의미
- this@{클래스 이름}으로 다른 클래스를 가리킬 때 사용 가능
정적 필드 및 메소드
- 클래스 내에 상수를 정의하거나 인스턴스 생성 없이 사용할 수 있는 메소드를 만들기 위해 사용
- companion object : 클래스별로 하나씩 정의할 수 있는, 클래스의 인스턴스 생성 없이 사용할 수 있는 오브젝트(object)
//생성자가 private으로 선언되어 있으므로 외부에서는 접근 불가능
class User private constructor(val name: String, val registerTime: Long) {
//companion object는 클래스 내부에 존재하므로 private 생성자에 접근 가능
companion object {
fun create(name:String): User {
return User(name, System.currentTimeMillis())
}
}
}
//함수 호출 방법
User.create("yejin")
제네릭
- 인자로 사용하는 타입에 따라 구체화되는 클래스나 인터페이스를 의미
- 코틀린에서는 반드시 제네릭 클래스에 타입을 넣고 선언을 해야함
//컴파일 성공
val names: List<String>
val entries: Map<String, String>
//제네릭 클래스 타입을 넣지 않아 컴파일 실패
val names: List
- 제네릭을 클래스/인터페이스 정의
//Car 클래스 정의
class Car { }
//항목을 담거나 빼는 함수가 있는 제네릭 인터페이스 Container 정의
//interface Container<T: Car>로 정의한다면 Container인터페이스는 Car 클래스 및 그 하위 클래스만 받을 수 있음
interface Container<T> {
fun put(item: T)
fun take(): T
}
//Car를 담거나 뺄 수 있도록 인터페이스를 구현하는 Garage 클래스 정의
class Garage: Container(Car) {
override fun put(item: Car){
}
override fun take(): Car {
}
}
- 타입이 정의되어 있는 제네릭을 인자로 받거나 호출 시점에 타입을 지정하는 함수 정의
//타입이 정의되어 있는 제네릭을 인자로 받는 함수
fun processItems(items: List<String>){
}
//호출시점에 타입이 정해지는 제네릭을 인자로 받는 함수
fun <T> processItems(items:List<T>){
}
- 호출 시점에 타입이 정해지는 제네릭을 인자로 받는 경우, 하위 또는 상위 타입을 받도록 지정 가능
- in T : T의 상위 타입 제한, input의 약자이며 write만 가능
- out T : T의 하위 타입 제한, output의 약자이며 read만 가능
open class Car {...}
class Sedan: Car() {...}
class Truck: Car() {...}
//src로 받은 목록을 dest에 추가
//dest는 T의 상위 타입을 제한, src는 T의 하위타입을 제한한다
fun <T> append(dest: MutableList<in T>, src: List<out T>) {
dest.addAll(src)
}
val cars: MutableList<Car> = ...
val sedan: List<Sedan> = ...
val truck: List<Truck> = ...
append(cars, sedan)
append(cars, truck)
예외
- throw로 예외를 발생시키고, try-catch 및 finally 문을 사용하여 예외 처리
//throw로 IllegalArgumentException를 발생시키는 예시
fun checkAge(age: Int) {
if(age<0){
throw IllegalArgumentException
("Invalid age: $age")
}
val valid: Boolena = try {
//예외를 발생시키는 코드
...
//예외가 발생하지 않았을 경우 true 반환
} catch (e: Exception) {
//예외가 발생했을 때 수행할 동작
...
//동작 수행 후 false 반환
false
} finally {
//예외 발생 여부와 상관없이 수행할 동작
}
NULL 안정성
- 코틀린에서는 모든 타입에 명시적으로 널 허용 여부 표기 ex) @Nullable, @NonNull
- 별도 표기가 없을 경우 널 값을 허용하지 않으므로, 널 값을 가질 수 있고록 하려면 타입 뒤에 ?를 붙여줘야 함
//컴파일 성공
val nullableString: String? = null //null값을 가질 수 있도록 '?'표기
val nonNullString: String = "Foo" //null값이 아닌 값으로 초기화
//컴파일 실패
val name: String //null을 허용하지 않는 값을 초기화하지 않아 에러 발생
val address: String = nul //null을 허용하지 않는 값에 nulldmf 대입했으므로 에러 발생
- 함수의 파라미터와 반환 값에도 동일하게 적용
//line2의 인자로 null값 허용
fun formatAddress(line1: String, line2: String?, city: String): String {...}
//입력한 주소에 해당하는 우편번호를 반환하지만, 검색 결과가 없을 경우 null 반환
fun findPostalCode(address: String): PostalCode? {...}
//line1의 인자로는 null을 허용하지 않으므로 에러 발생
formatAddress(null, null, "Seoul")
//postaclCode 값은 null을 허용하지 않으나, findPostalCode 함수는 null값 반환 가능
val postalCode: PostalCode = findPostalCode("1600 Amphitheatre Pkwy")
- 엘비스 연산자(?:) : 널 값을 허용하지 않는 값 혹은 변수에 널 값을 반환할 수 있는 함수의 결과를 처리하기 위해 사용
//기본 형식
foo ?: bar //foo가 null이 아닐 경우에는 foo를, null이라면 bar를 반환
//입력한 주소에 해당하는 우편번호를 반환하지만, 검색 결과가 없을 경우 null 반환
fun findPostalCode(address: String): PostalCode? {...}
//findPostalCode가 null값을 반환하는 경우 PostalCode.NONE값을 postal에 대입
val postal1: PostalCode = finfPostalCode("1600 Amphiteeatre Pkwy") ?: PostalCode.NONE
fun generateMapImage(address: String): Image? {
//우편변호 검색 결과가 없을 경우 바로 함수 실행을 종료하고 결과로 null 반환
val postal2 = findPostalCode(address) ?: return null
...
}
- 안전한 호출(?.) 연산자 : 객체가 널 값이 아닌 경우에만 연산자 뒤의 문장을 수행하여, 널 값 확인과 값 접근/함수 호출을 한번에 수행
//기본 형식
val foo = bar?.baz //bar가 null이 아닐 경우에만 bar.baz를 대입, 그렇지 않으면 null 대입
foo?.bar() //foo가 null이 아닐 경우에만 bar() 호출
- 안전한 자료형 변환(as?) 연산자 : 자료형 변환이 실패할 경우 널 값을 반환하여, 불가능한 자료형 변환을 해결
val foo: String = "foo"
val bar: Int? = foo as? Int //자료형 변환에 실패하면 null값이 대입되므로 bar가 null값을 허용하도록 Int?로 정의
val bar: Int = foo as? Int ?: 0 //변환에 실패할 경우 기본값을 0으로 지정
- 널 값이 아님을 명시하는 비 널 값 보증(!!) : 널 값을 포함할 수 있는 타입을 널 값을 포함하지 않는 타입으로 변환하여 사용
//foo는 null을 포함할 수 있는 Foo 타입
val foo: Foo? = ...
//값 foo는 null값을 포함하지 않음을 보장
val nonNullFoo: Foo = foo!!
//값 foo가 null이 아님을 보장하면서 bar() 함수 호출
foo!!.bar()
//값 foo가 null이 아님을 보장하면서 baz 프로퍼티에 접근
val myBaz = foo!!.baz
//address와 line2 모두 null이 아님을 보장하지만 중첩 사용은 권장하지 않음
val line: String = contact.address!!.line2!!
'Android' 카테고리의 다른 글
[kotlin] 코틀린을 코틀린답게 (라이브러리 - 조건 확인 함수,컬렉션 생성 함수) (0) | 2020.03.08 |
---|---|
[kotlin] 안드버디 스터디 정리 - 1 (0) | 2020.03.05 |
[kotlin] 코틀린을 코틀린답게 (함수,람다표현식) (0) | 2020.03.05 |
[kotlin] 코틀린을 코틀린답게 (클래스,프로퍼티,접근제한자,생성자) (0) | 2020.03.03 |
[kotlin] 코틀린을 코틀린답게 (변수,배열,컬렉션) (0) | 2020.02.28 |