티스토리 뷰
클래스
- 코틀린의 클래스는 자바와 매우 유사하지만 자바에는 없는 몇몇 기능들을 제공한다.
데이터 클래스
자료를 구성하는 프로퍼티만 선언하면 컴파일러가 equals(), hashCode(), toString() 함수를 자동으로 생성해준다.
자바에서 흔히 생성하였던 VO 클래스를 코틀린에서는 프로퍼티만 선언함으로써 구현할 수 있다.
// 데이터 클래스 선언 data class Person(val name: String, val address: String) private val shin1 = Person("Shin Kwanghee", "Seoul") private val shin2 = Person("Shin Kwanghee", "Seoul") private val shin3 = Person("Shin", "Seoul") private val kim1 = Person("Kim Minyoung", "Cheongju") @Test fun personDataTest() { println("shin1 == shin2 : ${shin1 == shin2}") println("shin hashCode = ${shin1.hashCode()} , ${shin2.hashCode()} , ${shin3.hashCode()}") println("kim.toString = ${kim1.toString()}") } ## 결과 shin1 == shin2 : true shin hashCode = 1455041812 , 1455041812 159626362 kim.toString = Person(name=Kim Minyoung, address=Cheongju)
생성자
자바와 동일하게 필요한 변수에 따라 생성자를 여러개 생성할 수 있다.
class ClassTest { private val name: String private val color: String constructor(name: String) { this.name = name this.color = "#000000" } constructor(name: String, color: String) { this.name = name this.color = color } }
- 커스텀뷰를 생성할떄는 @JvmOverloads 어노테이션을 통해 생성자를 모두 작성하지 않아도 된다
- @JvmOverloads는 생성자의 파라메터가 기본값으로 대체하도록 컴파일러에서 지시한다
class CustomView : View { constructor(context: Context) : this(context, null) constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( context, attrs, defStyleAttr ) { // ... } } class CustomView2 @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { // ... }
초기화 블록을 가진 주 생성자
생성자는 기본적으로 함수를 표현하는 기능이기 때문에 변수 초기화 이외에도 특정한 작업을 init 블록을 통해 수행할 수 있다.
class CustomView2 @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { init { // ... } }
- Gson을 사용할 때는 모든 프로퍼티에 초기화 디폴트값을 지정해주어야 init 블록을 호출한다
data class Documents( // @SerializedName("collection") // val collection: String, // // @SerializedName("datetime") // val dateTime: String, // // @SerializedName("display_sitename") // val displaySiteName: String, // // @SerializedName("doc_url") // val docUrl: String, // // @SerializedName("width") // val width: Int, // // @SerializedName("height") // val height: Int, // // @SerializedName("image_url") // val imageUrl: String, // // @SerializedName("thumbnail_url") // val thumbnailUrl: String @SerializedName("collection") val collection: String = "", @SerializedName("datetime") val dateTime: String = "", @SerializedName("display_sitename") val displaySiteName: String = "", @SerializedName("doc_url") val docUrl: String = "", @SerializedName("width") val width: Int = 0, @SerializedName("height") val height: Int = 0, @SerializedName("image_url") val imageUrl: String = "", @SerializedName("thumbnail_url") val thumbnailUrl: String = "" ) { init { println("=== Init Block ===") } }
프로퍼티의 사용자 지정 Getter/Setter
기본적으로 프로퍼티에는 내부에 저장된 필드 값을 가져오거나 설정할 수 있도록 Getter/Setter를 내부적으로 구현하고 있다.
사용자 지정 Getter/Setter를 사용하면 프로퍼티의 Getter/Setter의 구현을 원하는대로 변경할 수 있으며, 특정 개체의 값에 따른 다양한 정보를 속성 형태로 제공할 때 유용하다.
사용자 지정 Getter/Setter는 프로퍼티의 선언과 함께 get() 및 set(value)를 사용하여 선언할 수 있다.
// 형태 var <propertyName>[: <PropertyType>] [<property_initializer>] [<getter>] [<setter>] // 예시 data class Person( val age: Int ) { val adult: Boolean // 성인 여부를 age 값이 19보다 크거나 같을 경우에만 true로 반환 get() = age >= 19 var address: String = "a, b, c" get() = "주소 = $field" set(value) { // 인자로 들어온 문자열의 앞 10 자리만 필드에 저장 field = value.substring(0..9) } } private val shin = Person(14) @Test fun personWithAgeTest() { shin.address = "서울특별시 강서구 화곡" println("adult : ${shin.adult} , address : ${shin.address}") } ## 결과 adult : false , address : 주소 = 서울특별시 강서구
추상 클래스
자바에서 사용하는 abstract와 같은 개념으로 코틀린 class 앞에 open 키워드를 붙이거나 abstract를 붙여 사용할 수 있다.
open class Base { open fun f() {} } abstract class Derived : Base() { override abstract fun f() }
싱글톤 클래스
싱글톤 : 단 하나의 인스턴스만 생성되도록 제약을 둔 디자인 패턴
코틀린에선 오브젝트(object)를 사용하여 싱글톤을 간편하게 선언 할 수 있다.
object today { val day = "29" fun getTime() { ... } } val dayValue = today.day today.getTime()
enum 클래스
enum 클래스에 프로퍼티를 추가할 수 있다.
enum class Color { RED, BLUE, GREEN } enum class Color(val name: String) { RED, BLUE, GREEN }
어노테이션 클래스
어노테이션에 멤버를 가질 수 있다.
자바에서는 멤버수가 하나이면서 멤버이름이 value인 경우에만 바로 값을 대입할 수 있지만, 코틀린은 상관없이 바로 값을 대입할 수 있다.
annotation class AnnotationExample { val name: String } @AnnotationExample("SHIN KWANGHEE") class Example() { ... }
- 멤버에 기본값을 지정하는 경우 멤버의 기본값을 참조한다.
annotation class AnnotationExample { val name: String = "SHIN KWANGHEE" } @AnnotationExample() class Example() { ... }
코틀린에서 어노테이션을 사용할 수 있는 타입은 자바와 동일
자바 원시 타입에 대응하는 타입(Int, Long 등)
문자열(String)
클래스
enum 클래스
멤버가 속한 어노테이션이 아닌 다른 어노테이션 클래스
위에 나열된 타입으로 구성된 배열
코틀린에서는 배열 타입의 멤버가 포함하는 타입에 따라 값을 지정하는 방식이 달라 진다
자바 원시타입에 대응하는 경우 원시타입 전용 배열클래스 사용(IntArray 또는 LongArray 등)
그 외의 타입은 일반배열 사용
annotation class AnnotationExample {
val numbers: IntArray,
val colors: Array
}
@AnnotationExample(numbers = intArrayOf(1, 2, 3),
colors = arrayOf("RED", "BLUE", "GREEN"))
class Example() {
...
}
- 부가 정보를 표시하기 위해 메타 어노테이션을 지정할 수 있다.
@Target(AnnotationTarget.TYPE) @Retention(AnnotationRetention.SOURCE)
@Repeatable
@MustBeDocumented
annotation class Example
- 코틀린에서 주 생성자에 어노테이션을 사용하는 경우 constructor 키워드 앞에 넣는다.
// 주 생성자 앞에 어노테이션을 추가
class Example @AnnotationExample constructor(val name: String) {
...
}
- 프로퍼티와 생성자, 함수의 파라메타에도 어노테이션을 지정할 수 있다.
중첩 클래스
정적 중첩 클래스(static nested class)의 경우 자바는 static 키워드를 붙이지만, 코틀린은 별도의 키워드를 붙이지 않는다.
비 정적 중첩 클래스(non-static nested class)의 경우 코틀린에는 inner 키워드를 추가해야 한다.
class Outer {
// 키워드가 없으면 정적 중첩 클래스
class StaticNested {
...
}
// 비 중첩 클래스
inner class NonStaticNested() {
...
}
// 정적 중첩 클래스 : Outer클래스의 인스턴스 생성 없이 인스턴스를 생성 가능
val staticInstance = Outer.StaticNested()
// 비 중첩 클래스 : Outer 클래스의 인스턴스를 생성해야 인스턴스를 생성 가능
val nonStaticInstance = Outer().NonStaticNested()
}
한정 클래스
enum 클래스의 개념을 확장
종류별로 하나의 인스턴스만 생성되어있는 enum 클래스와 달리 한정 클래스는 여러 개의 인스턴스를 생성할 수 있다.
enum 클래스의 특징을 그대로 가지고 있기에, 이를 상속하는 클래스는 한정 클래스로 정의되는 여러 종류 중 하나로 취급된다.
한정 클래스 사용 예시
MobileApp : 한정 클래스
Android, IOS : 이를 상속하는 클래스
```
sealed class MobileApp(val os: String) {
class Android(os: String, val packageName: String) : MobileApp(os)
class IOS(os: String, val bundleId: String) : MobileApp(os)
}
```
한정 클래스는 일반적으로 클래스 내에 중첩하여 선언하지만, 같은 파일내에서 정의를 한다면 클래스 외부에서도 선언할 수 있다.
한정 클래스는 한정 클래스로 정의된 클래스의 종류에 따라 다른 작업을 처리해야 할 때 유용하다.
fun whoami(app: MobileApp) = when (app) {
is MobileApp.Android -> println("${app.os} / ${app.packageName}")
is MobileApp.IOS -> println("${app.os} / ${app.bundleId}")
// 모든 경우를 처리했으므로 else를 쓰지 않아도 됩니다.
}
한정 클래스의 종류를 모두 구현하지 않은 경우 else 절을 추가해야 한다.
else 문을 추가하지 않으려면 한정 클래스의 종류를 모두 구현해야 하며, else 문 또는 종류를 모두 구현하지 않은 경우 컴파일 에러가 발생한다.
lateinit을 통한 프로퍼티 지연 초기화
프로퍼티 선언 시점이나 생성자 호출 시점에 값을 할당할 수 없는 경우 'lateinit' 키워드를 사용하여 나중에 할당될 값임을 명시한다
'lateinit' 키워드는 var 프로퍼티에서만 사용 가능
'lateinit' 키워드를 사용한 프로퍼티를 초기화 없이 사용하려 한다면 Uninitialized PropertyAccessException 예외가 발생한다.
class Person { val name : String? = null // val 프로퍼티는 항상 선언과 함께 값을 할당해야 한다. lateinit var address : String? // 선언 시점에 값을 할당하지 않아도 컴파일 에러가 발생하지 않는다. }
lazy를 통한 프로퍼티 지연 초기화
lateinit을 통해 프로퍼티를 선언할때는 var로 선언해야 되기때문에 언제든 값이 변경될 수 있는 단점이 존재함
읽기 전용의 val로 선언한 객체나 프로퍼티를 나중에 초기화 하기 위해 lazy를 사용
private val viewModel : DetailViewModel by lazy { DetailViewModel(ImageLoadingInterface(this, cacheSize)) }
클래스 및 인터페이스
코틀린에서 접근 제한자를 지정하지 않는 경우 public
## Java public class Example { ... } ## Kotlin class Example { ... }
- 클래스와 인터페이스는 본체 없이 선언하는것이 가능
class MainActivity interface OnUpdateView
자바와는 다르게 클래스의 인스턴스를 생성할때 new 키워드를 사용하지 않는다
## Java Today today = new Today(); Message message = new Message("Hello World"); ## kotlin val foo : Today = Today() // new 키워드 생략 val bar : Message = Message("Hello World") // 인자 하나를 받는 생성자로 인스턴스 생성
추상클래스 선언
## Java abstract class FileChoose { public abstract void getVideo(); } // 추상 클래스의 인스턴스 생성 // 클래스 생성과 동일하게 new 사용 FileChoose choose = new FileChoose() { @Override public void getVideo() { } }; ## Koltin abstract class FileChoose { abstract fun getAudio() } // 추상 클래스의 인스턴스 생성 // object: [생성자] 형태로 선언 val choose = object: FileChoose() { override fun bar() { // 함수 구현 } }
인터페이스 선언
## Java public interface OnDownloadCompleteListener { void onSuccess(); void onFailure(); } // 인터페이스 인스턴스 생성 // 클래스 생성과 동일하게 new 사용 OnDownloadCompleteListener listener = new OnDownloadCompleteListener() { @Override public void onSuccess() { ... } @Override public void onFailure() { ... } }; ## Kotlin interface OnDownloadCompleteListener { fun onSuccess() fun onFailure() } // 인터페이스의 인스턴스 생성 // object: [인터페이스 이름] 형태로 선언 val listener : OnDownloadCompleteListener { override fun onSuccess() { ... } override fun onFailure() { ... } }
접근 제한자
접근 제한자가 없으면 기본적으로 public으로 간주한다.
internal 접근 제한자는 동일한 모듈 내에 있는 클래스들로의 접근을 제한
- internal 접근 제한자가 제한하는 모듈의 범위
- IntelliJ IDEA 모듈
- Maven / Gradle 모듈
- 하나의 Ant 태스크 내에서 함께 컴파일 되는 파일들
## Java public class Example { public int a = 1; protected int b = 2; private int c = 3; int d = 4; // 패키지 단위 제한자(별도 표기 없음) } ## Kotlin class Example { var a = 1 // 접근 제한자가 없으면 public protected var b = 2 private val c = 3 internal var d = 4 }
- internal 접근 제한자가 제한하는 모듈의 범위
생성자
생성자에 인자가 필요한 경우 클래스 선언할 때 추가 가능
코틀린에서는 이를 주 생성자(primary constructor)라고 한다
## Java public class Example { public Example(String today) { Log.d(TAG, "Today : " + today) } } ## Kotlin class Example(today : String) { init { Log.d(TAG, "Today : $today") } }
- 코틀린에서는 생성자의 인자를 바로 클래스 내부의 프로퍼티에 값을 할당할 수 있다.
class Example(today : String, time : String)
- 주 생성자 외의 다른 형태의 생성자가 필요한 경우 constructor 키워드를 사용해서 추가 생성자를 선언할 수 있다.
class Example2(today : String, time : String) { // today 값만 인자로 받는 추가 생성자 constructor(today: String) : this(today, "") // 두 인자의 값을 모두 0으로 지정하는 추가 생성자 constructor() : this("", "") }
- 코틀린에서 추가 생성자를 정의하는 경우 주 생성자를 반드시 호출해야 한다.
- 추가 생성자에서 인자와 프로퍼티를 함께 선언할 수 없다
- 생성자의 가시성을 변경하려면 constructor 키워드 앞에 접근 제한자를 추가한다.
// 주 생성자의 가시성을 internal로 변경하는 경우 constructor 키워드를 표기해야 한다. class Example internal constructor(today : String, time : String) { private constructor(today: String) : this(today, "") // 접근제한자를 지정하지 않았으므로 public constructor() : this("", "") }
함수
특별한 타입을 반환하지 않는 함수는 Unit 타입 반환
Unit 타입은 반환 표시를 생략할 수 있다.
class Example { // 아무것도 반환하지 않는 함수 fun temp() : Unit { ... } // 반환 타입 생략 fun data() { ... } // 정수값을 반환 private fun getPosition(position : Int) : Int { return position } }
상속 및 인터페이스 구현
콜론(:) 뒤에 상속한 클래스나 구현한 인터페이스를 표기
클래스를 상속하는 경우 반드시 부모 클래스의 생성자를 호출해야 한다.
class MainActivity : AppCompatActivity(), View.OnClickListener { ... }
부모 클래스의 생성자가 여러 형태인 경우, 별도의 생성자 선언에서 부모 클래스의 생성자를 호출하도록 구현할 수 있다.
부모 클래스의 생성자는 자바와 동일하게 super 키워드를 사용하여 호출
생성자가 여럿인 경우 this 키워드를 사용하여 자기 사신의 생성자를 호출할 수 있다.
코틀린에서는 상속받거나 구현된 함수 앞에 무조건 override 키워드를 붙이도록 강제한다.
class CustomView : View { constructor(context: Context) : this(context, null) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { // 뷰 초기화 } }
코틀린에서는 open 키워드를 붙인 클래스나 함수가 아니라면 상속하거나 함수를 재정의할 수 없다.
open class BaseActivity : AppCompatActivity() { open val message = "Hello World" ... } class MainActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Log.d(TAG, "Message : $message") } }
this
해당 키워드를 사용한 클래스 자신을 지칭할 때 사용
키워드를 사용하는 위치에 따라 this가 의미하는 클래스가 달라질 수 있어 this@{클래스 이름} 형태로 표기할 수 있다.
class MainActivity : BaseActivity(), View.OnClickListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) button.setOnClickListener(this) } override fun onClick(v: View?) { Toast.makeText(this@MainActivity, "Hello World", Toast.LENGTH_SHORT).show() } }
정적 필드 및 메서드
자바의 정적 필드와 메소드를 static 으로 선언할 수 있지만, 코틀린에서는 이를 지원하지 않는다.
일반적인 경우에는 클래스 내에 선언한 정적 필드 및 메서드를 패키지 단위로 선언할 수 있다.
const val name = "신광희" fun getName() {} class Example { fun data() { } }
- 패키지 단위로 선언한 값이나 함수는 패키지에 종속되므로 import문에서도 {패키지명}.{값 또는 이름}을 사용한다.
import com.example.name import com.example.getName ...
- 패키지 단위의 함수는 특정 클래스에 속해있지 않으므로, 클래스 내 private로 선언된 멤버에 접근해야 하는 팩토리 메서드(factory method)는 패키지 단위 함수로 구현 할 수 없다.
- 동반 객체(companion object)를 통해 클래스 내의모든 멤버에 접근할 수 있으면서 인스턴스 생성을 하지 않을 수 있다.
- 동반 객체 : 클래스 별로 하나의 클래스의 인스턴스 생성 없이 사용할 수 있는 오브젝트를 정의할 수 있는데 이를 동반 객체라고 한다.
class MainActivity : BaseActivity(), View.OnClickListener { ... companion object { fun loadMessage(message : String) : String { ... } } }
'프로그래밍 > Kotlin' 카테고리의 다른 글
[Kotlin] 흐름제어(조건문, 반복문) (0) | 2020.03.15 |
---|---|
[Kotlin] 고차함수 / 람다식, 코틀린의 다양한 함수 알아보기 (0) | 2020.03.15 |
[Kotlin] 변수와 자료형 / 연산자 (0) | 2020.03.15 |
[Kotlin] Kotlin Coding Convention (0) | 2019.08.03 |
[Kotlin] 컬렉션(Collection) (0) | 2018.12.30 |
- Total
- Today
- Yesterday
- 단어 길이 재기
- 10827
- #android #motionlayout
- 영상처리
- 1237
- 피보나치 수 4
- 1260
- 10757
- 최대공약수와 최소공배수
- 2743
- 자동타입
- 10826
- 1158
- #kotlin
- a^b
- algorithm
- 조세퍼스 문제
- 함수형사고 Kotlin Java
- javacv
- 문자열
- constraintlayout
- 큰 수 A+B
- GCD 합
- algorihtm
- 10828
- 알고리즘
- OpenCV
- mssql
- kotlin
- Eclipse
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |