-
코틀린을 코틀린답게 사용하는 법 - (1)안드로이드 2020. 2. 29. 18:48
지난 25기 솝트 앱잼 팀과 함께 개발한 '얼리버디' 개발을 다시 시작한다.
원래는 서버 개발자였는데, 안드로이드로 갈아 탐 ㅋㅋㅋ (미안 정욱, 예지,,)
앱 자체는 거의 다 완성 했지만, 디자인 패턴과 아키텍처를 공부하고 적용해서 개발을 하기로 했다.
일단 첫번째 목표는 3월 4일까지 제목에서 쓴 것 대로 '코틀린을 코틀린답게 사용하는 법' 익히기.
커리큘럼은 [커니의 코틀린]의 목차를 따라가는 걸로 !
- 2장 자바와 비교해보는 코틀린
- 3장 자바와는 다른 코틀린의 특징
- 4장 코틀린 표준 라이브러리
- 8장 코틀린 안드로이드 익스텐션
이번 포스팅에서는,
자바와 비교해보는 코틀린에 대해서 정리해보도록 하겠다.
컬렉션
1. 코틀린에서는 컬렉션 내 자료의 수정 가능 여부에 따라 컬렉션의 종류를 구분한다.
- 가변 타입(mutable): 컬렉션 내 자료 수정 가능
- 불변 타입(immutable): 컬렉션 내 자료 수정 불가능
2. 코틀린 표준 라이브러리에서 컬렉션을 쉽게 생성하는 함수를 제공한다.
원하는 컬렉션 타입 + Of() 함수로 생성할 수 있는데, 예시는 아래와 같다.
// 자료를 수정할 수 없는 리스트 생성 val immutableList : List<String> = listOf("hello", "hiroo") // 컴파일 에러: 자료 수정 X immutableList.add("hi") // 자료를 수정할 수 있는 리스트 생성 val mutableList : MutableList<String> = arrayListOf("one", "two") // 자료 수정 O mutableList.add("three")
3. 코틀린에서는 컬렉션 내 항목에 get, set 메서드를 사용하지 않고 인덱스로 바로 접근할 수 있다.
그리고 맵은 숫자 인덱스 대신 키 값을 넣어 항목에 접근할 수 있다.
val immutableMap : Map<String, Int> = mapOf(Pair("A", 65), Pair("B", 66)) // code = 65 val code = immutableMap("A") // 컴파일 에러: 자료 수정 X immutableMap["C"] = 67 val mutableMap : HashMap<String, Int> = hashMapOf(Pair("A", 65), Pair("B", 66)) // 자료 수정 O mutableMap["C"] = 67
💡맵을 생성하는 함수들은 키와 값을 인자로 받기 위해 Pair 클래스를 사용하는데, 이때 코틀린 표준 라이브러리에서 제공하는 to 함수를 사용하면 Pair 형태의 값을 더 편리하게 생성할 수 있다.
val map = mapOf("A" to 65, "B" to 66)
클래스 및 인터페이스
클래스와 인터페이스의 선언 및 인스턴스 생성
1. 코틀린에서는 자바에서 클래스의 인스턴스를 생성하기 위해 사용한 new 키워드를 사용하지 않는다.
2. 추상 클래스(abstract class)는 자바와 동일한 방법으로 생성하지만, 추상 클래스의 인스턴스를 생성하는 형태는 매우 다르다.
- 자바 - 클래스 생성과 동일하게 new 사용
- 코틀린 - object: [생성자] 형태로 선언
// 추상 클래스 선언 abstract class Foo { abstract fun bar() } // 추상 클래스의 인스턴스 생성 val foo = object: Foo() { override fun bar() { // do something } }
3. 인터페이스를 선언하고, 인터페이스의 인스턴스를 생성하는 방법은 추상클래스와 거의 똑같다.
단, 추상 클래스에서는 인스턴스 생성시 생성자를 사용하지만, 생성자가 없는 인터페이스는 인터페이스의 이름만 사용한다.
// 인터페이스 선언 interface Bar { fun baz() } // 인터페이스의 인스턴스 생성 val bar = object : Bar { override fun baz() { // do something } }
프로퍼티(property)
자료를 저장할 수 있는 필드와 이에 상응하는 getter/setter 메서드 함께 제공
- 프로퍼티의 선언
- val: 값을 읽을 수만 잇음
- var: 값을 읽고 쓰는 게 모두 가능
- lateinit var: 프로퍼티 선언 시점이나 생성자 호출 시점에 값을 할당할 수 없는 경우 lateinit 키워드 사용. 값은 나중에 할당
val name = "양시연" // 값이 할당된 뒤 변경 불가 var age : Int? = null // null만으로는 타입을 추론할 수 없으므로 타입 선언 필요 age = 24 // 변경 가능 lateinit var address : String
생성자
1. 코틀린은 init 블록을 사용하여 기본 생성자를 대체한다.
2. 코틀린에서는 생성자의 인자를 통해 바로 클래스 내부의 프로퍼티에 값을 할당 할 수 있다.
이 경우 생성자의 인자를 통해 프로퍼티 선언을 대신하므로 추가로 프로퍼티를 선언하지 않아도 된다.
class Foo(val a: Int, var b: String)
상속 및 인터페이스 구현
코틀린에서는 클래스의 상속과 인터페이스의 구현을 구분하지 않고, 콜론(:) 뒤에 상속한 클래스나 구현한 인터페이스를 표기한다.
클래스를 상속하는 경우 반드시 부모 클래스의 생성자를 호출해야하며, 자바와 동일하게 super 키워드를 사용하여 호출한다.
class MyActivity : AppCompatActivity(), View.OnClickListener { // AppCompatActivity의 onCreate() 메서드 상속 override fun onCreate(savedInstanceState: Bundle?){ super.onCreate(savedInstanceState) } // View.OnClickListener 인터페이스 구현 override fun onClick(v: View){ } }
정적 필드 및 메서드
코틀린에서는 자바의 정적 필드 및 메서드를 패키지 단위로 선언할 수 있다.
하지만, 패키지 단위 함수는 특정 클래스에 속해있지 않으므로, 클래스 내 private로 선언된 멤버에 접근해야하는 팩토리 메서드(factory method)는 패키지 단위 함수로 구현할 수 없다. 이 경우 동반 객체(companion object)를 사용한다.
동반 객체(companion object)란?
코틀린은 클래스 내에 정적 필드나 정적 함수를 둘 수 없는 대신에 클래스 별로 하나씩 클래스의 인스턴스 생성 없이 사용할 수 있는 오브젝트(object)를 정의할 수 있는데, 이를 동반 객체(companion object)라고 한다.
class User private constructor(val name: String, val registerTime: Long) { companion object { // companion object는 클래스 내부에 존재하므로 private으로 선언된 생성자에 접근 가능 fun create(name: String) : User { return User(naem, System.currentTimeMillis()) } } }
💡정적 필드 및 메서드는 왜 필요한가?
클래스 내에 상수를 정의하거나 인스턴스 생성 없이 메서드를 호출하고 싶을 때 사용
🙋🏻♀️내가 companion object를 쓰는 법
ViewPager로 Fragment들을 붙일 때 가끔 특정 값을 Fragment에 넘겨줘야 할 때가 있다.
이때, setter나 생성자로 바로 값을 넘겨주면 restore할 때 불안전 하다고 해서 (어디서 봤던건지 기억안남)
이 경우에는 companion object의 newInstance 메서드를 사용해서 Fragment 인스턴스를 생성해주고,
넘겨줘야하는 특정 값을 Bundle로 넘긴다.
companion object { fun newInstance(from: String): ClubCategoryFragment { val fragment = ClubCategoryFragment() val bundle = Bundle() bundle.putString("from", from) fragment.arguments = bundle return fragment } } // newInstance 호출하는 부분 ClubCategoryFragment.newInstance("write")
싱글톤
싱글톤은 단 하나의 인스턴스만 생성되도록 제약을 둔 디자인 패턴이다.
코틀린에서는 오브젝트(object)를 사용하여 간편하게 선언할 수 있다.
자바에서는 싱글톤 패턴을 만족시키기 위해 인스턴스 변수를 선언하고, 생성자 및 정적 메서드를 추가하는 작업을 넣어야 한다.
public final class Singleton { private static Singleton instance = null; private Singleton() {} public static synchronized Singleton getInstance() { if(instance == null){ instance = new Singleton() } return instance; } }
하지만 코틀린은 위 작업을 단 한줄로 처리할 수 있다.
object Singleton
중첩 클래스
특정 클래스 간 종속관계가 있는 경우 이를 중첩 클래스(nested class)로 표현할 수 있다.
단, 자바와 달리 중첩 클래스의 종류에 따라 사용하는 문법이 살짝 다르다.
- 정적 중첩 클래스(static nested class): 별도의 키워드 필요 X. 바깥 클래스의 인스턴스 생성 필요 없이 인스턴스 생성 가능
- 비 정적 중첩 클래스(non-static nested class): inner 키워드 추가. 바깥 클래스의 인스턴스를 생성해야 인스턴스 생성 가능
class Outer { // 키워드가 없으면 정적 중첩 클래스로 간주 class StaticNested { } // inner 키워드를 사용하여 비 정적 중첩 클래스 선언 inner class NonStaticNested { } } // 정적 중첩 클래스 val staticInstance = Outer.StaticNested() // 비 정적 중첩 클래스 val nonStaticInstance = Outer().NonStaticNested()
💡중첩 클래스를 쓰는 이유
1. 클래스들의 논리적인 그룹을 나타내기 위해. 나는 주로 RecyclerViewAdapter안에 ViewHolder 클래스를 비 정적 중첩 클래스로 만들어준다.
2. 향상된 캡슐화
3. 가독성이 좋아지고, 유지보수가 편해짐
3/4(수)까지 커니의 코틀린 2,3,4,8장을 공부하기로 했는데 아직 2장 반도 못했다 ^,^ 망한듯. ㅋㅋㅋㅋ
하지만 어제 애들이랑 만나서 서로 퀴즈를 내주면서 공부했는데 아주아주 즐거웠다. 얼리버디 012❤️
'안드로이드' 카테고리의 다른 글
안드로이드 프로젝트에 RxJava 적용하기 (1) 2020.04.12 '슥삭'의 아키텍쳐 (1) MVVM 디자인패턴 (0) 2020.03.11 코틀린을 코틀린답게 사용하는 법 - (4) 표준 라이브러리 (0) 2020.03.11 코틀린을 코틀린답게 사용하는 법 - (3) (0) 2020.03.08 코틀린을 코틀린답게 사용하는 법 - (2) (1) 2020.03.03