ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 코틀린을 코틀린답게 사용하는 법 - (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❤️

     

     

     

     

     

     

Designed by Tistory.