Kotlin
Jetbrain에서 Java를 대체하기 위해 만든 언어로 아래와 같은 특징이 있다
- 널 안정성
- 가변/불변 구분(var, val)
- 람다식
- 스트림 API
- 클래스 프로퍼티에 대해 getter/setter 제공
특이한 점은 Kotlin은 원시 타입을 모두 객체로 표현한다.
예를들어, 아래와 같이 객체(Int)로 선언한다.
val foo : Int = ...
이렇게 표현할 경우 비효율적일거 같지만, 컴파일 과정에서 가장 효율적인 타입으로 변화된다.
위의 코드는 아래와 같이 표현된다.
int foo = ... ;
Java와의 비교
배열
Java의 경우 아래와 같이 배열 자료형이 별도로 존재한다.
int[] num = new int[]{1,2,3};
하지만, Kotlin의 경우 Array 객체로 표현한다.
val num : Array<Int> = arrayOf(1,2,3)
컬렉션(Collections)
Kotlin에선 Java의 컬렉션 클래스들을 그대로 사용할 수 있다. 다만, 컬렉션 내 자료를 수정할 수 있는지 여부에 따라 가변 타입(mutable)과 불변 타입(immutable)로 나뉜다.
자료구조 | 수정 불가 | 수정 가능 |
List | List | MutableList |
Map | Map | MutableMap |
Set | Set | MutableSet |
lateinit
가변타입 변수를 이용할 때 사용 가능한 키워드이다. 해당 키워드를 사용할 경우, 변수 선언 단계에서 초기화를 하지 안하도 된다. 하지만, 이 값이 초기화되지 않고 사용된다면 예외가 발생할 것이다. 이는 컴파일 단계에서 확인되지 않는 문제이므로 lateinit을 사용한 변수는 초기화가 되었는지 확인하는 코드가 필수적이다.
lateinit var address : String?
클래스와 생성자
Java에서는 메서드를 정의하는 것과 유사하게 생성자를 정의한다.
public class Foo{
public Foo(){
}
}
코틀린의 경우 init 블록을 통해 디폴트 생성자를 대체한다. 또한, 인자가 필요한 경우 클래스 우측에 명시한다.
class Foo(a:Int){
init{
Log.d("FOO", "Number:$a")
}
}
만약, 클래스에 프로퍼티를 인자로 받아 바로 할당하는 것을 원한다면 아래와 같이 작성 가능하다.
class Foo(val a:Int, val b:Char){
}
아래는 위와 같은 기능을 하는 Java 코드이다.
public class Foo{
int a;
char b;
public Foo(int a, char b){
this.a = a;
this.b = b;
}
}
메인 생성자 외에 다른 형태의 매개변수를 받는 생성자가 필요한 경우 아래와 같이 정의 가능하다.
class Foo(val a:Int, val b:Char){
//a의 값만 인자로 받는 생성자
constructor(a:Int):this(a,0)
//두 인자의 값을 모두 0으로 설정하는 생성자
constructor():this(0,0)
}
위 처럼 새로운 생성자를 사용할 경우 주 생성자 this를 통해 필수적으로 호출해야된다.
상속
Java의 경우 final 키워드를 통해 다른 클래스들이 상속을 할 수 없게한다. 하지만, Kotlin의 경우 기본적으로 모든 클래스는 상속 불가능한 상태로 정의되며, 상속을 허용하려면 open 키워드를 사용해야 한다.open 키워드가 붙이 않은 클래스나 함수가 아니라면 클래스나 함수를 재정의할 수 없다.
open class OpenClass{
//상속한 클래스에서 재정의 가능
open val openProperty = "foo"
//상속한 클래스에서 재정의 불가
val finalProperty ="bar"
//상속한 클래스에서 함수를 재정의 가능
open fun openFunc(){}
//상속한 클래스에서 함수 재정의 불가
fun finalFunc(){}
}
class FinalClass : OpenClass(){
override val openProperty = "FOO"
//override val finalProperty ="BAR" - 불가
//함수를 재정의 가능
open fun openFunc(){Log.d("Log","openFunc()")}
//fun finalFunc(){Log.d("Log","finalFunc()")} - 불가
}
this
this는 가장 가까운 범위의 클래스를 의미한다. 보통, this만 사용할 경우 키워드를 사용하는 위치에 따라 this가 의미하는 클래스가 달라지기 때문에 Java에서는 클래스명.this를 사용한다. Kotlin에서는 대신 this@클래스명 형태로 표기한다.
companion object
Kotlin의 경우 Java와 달리 정적(static) 필드, 함수를 클래스 내에 둘 수 없다. 대신, 클래스별로 클래스 인스턴스 생성 없이 사용할 수 있는 오브젝트(object)를 정의할 수 있는데, 이를 companion 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())
}
}
}
싱글톤(singleton)
싱글톤은 단 하나의 객체만 생성되도록 제약을 둔 디자인 패턴이다. Java에서는 싱글톤 객체를 생성하기 위해 복잡한 작업이 필요하지만, Kotlin에서는 이를 object를 통해 간편하게 사용할 수 있다.
//싱글톤
object Foo {
val FOO = "foo"
fun foo() { }
}
//실사용
val fooVlaue = Foo.FOO
Foo.foo()
enum
Java에서는 enum이라는 자료형이 존재하지만, Kotlin의 경우 enum class를 사용한다. 선언 형태만 다르고 사용법은 완전히 일치한다.
enum class Direction{
NORTH, SOUTH, WEST, EAST
}
중첩 클래스
Java에서는 특정 클래스 간 종속관계가 존재할 겨웅 중첩 클래스로 이를 표현할 수 있다.
클래스의 특징에 따라 정적 중첩 클래스와 비정적 중첩 클래스로 나뉜다.
- 정적 중첩 클래스는 외부 자원을 사용할 수 없고, Outer 클래스 없이 단독으로 인스턴스화 가능하다.
- 비정적 중첩 클래스는 외부 자원을 사용할 수 있조, Outer 클래스가 인스턴스화 되어야 Outer 내에서 인스턴스화 가능하다.
class Outer{
//정적 중첩 클래스
class StaticNested{
}
//비정적 중첩 클래스 --> inner class: 내부에서만 유효한 클래스
inner class NonStaticNested{
}
}
//정적: Outer 인스턴스 없이 생성
val staticInstance = Outer.StaticNested();
//비정적: Outer 인스턴스 생성 후 생성
val nontStaticInstance = Outer().StaticNested();
내용 일부는 '커니의 코틀린' 도서를 참조하였습니다.
'안드로이드 > Kotlin' 카테고리의 다른 글
[Kotlin] 컬렉션(Collections) (3) | 2020.12.21 |
---|---|
[Kotlin] 위임(Delegation)과 초기화 지연(lazy initialization) (0) | 2020.12.20 |
[Kotlin] 람다 표현식(lambda expression) (0) | 2020.12.20 |
[Kotlin] 널 안정성(Null safety) (0) | 2020.12.20 |
[Kotlin] Java와의 차이점 2 (0) | 2020.12.20 |