[커니의 Kotlin] Chap1.2
1부에 이어서 작성…
# 특징들
생성자
- 생성자 부분은 자바와 비교했을 때 조금 다른 면이 있다.
- 다음은 기본 생성자의 정의이다.
- init 블록을 사용해 기본 생성자를 대체한다.
- 생성자에 인자가 필요한 경우 인자처럼 받을 수 있다.
- 이를 주 생성자라고 부른다.
1 | class Test{ |
- 생성자의 인자를 통해 클래스 내부의 프로퍼티에 값을 할당할 수 있다.
- 생성자의 인자를 통해 프로퍼티 선언을 대신한다. 따라서 추가로 프로퍼티 선언이 필요 없다.
1 | class Test(val a: Int, val b: Char) |
- 생성자의 인자에서 프로퍼티 선언이 함께 이루어지고, 값 할당 또한 생성자 호출과 동시에 수행되므로 짧은 코드를 확인할 수 있다.
- 주 생성자 외에 다른 형태의 생성자가 필요한 경우
constructor
키워드를 사용해 선언할 수 있다.
1 | class Test(val a: Int, val b: Char){ |
- 이처럼 추가 생성자를 정의하는 경우 주 생성자를 반드시 호출해야 한다.(상속과 this를 사용해서!)
- 추가 생성자에서는 인자와 프로퍼티를 함께 선언할 수 없다. 따라서 프로퍼티 선언이 필요한 경우 주 생성자에서 이를 처리해야 한다.
- 생성자 앞에 접근 제한자를 붙여 가시성을 변경할 수 있다.
1 | class Test private constructor(val a: Int, val b: Char){ |
생성자는 중요한 개념이므로 추후 더 추가할 예정.
함수
- 메소드를 함수로 표현한다.
- void -> Unit이며 반환 값이 없음을 의미한다.
- 또한, 반환 값이 없는 함수는 Unit을 생략할 수 있다.
1 | class Test{ |
상속 및 인터페이스 구현
- 클래스의 상속과 인터페이스 구현을 구분하지 않고 콜론(:)으로 통일한다.
- 클래스를 상속받는 경우 반드시 부모 클래스의 생성자를 호출해야 하며, 부모 클래스의 생성자는 super 키워드를 사용해 호출한다.
1 | class MyView : View{ |
- 오버라이드 시 어노테이션을 사용하지 않고 override 키워드를 사용한다.
- open 키워드를 사용해 클래스 혹은 함수의 상속 여부를 결정할 수 있다.
- open 키워드가 있으면 상속 가능.
- open 키워드가 없으면 상속 불가능(Java의 final과 비슷)
this
- 해당 키워드를 사용한 클래스 자신을 지칭할 때 사용한다.
- 해당 위치에서 가장 가까운 범위의 클래스를 의미한다. 따라서 클래스 내에서 다른 클래스나 인터페이스의 객체를 동적으로 생성하여 사용하는 경우 키워드를 사용하는 위치에 따라 this가 의미하는 클래스가 달라질 수 있다.
- 따라서
this@{클래스이름}
처럼 명확하게 표시해준다.
동반객체
- 동반 객체(companion object)를 사용하면 클래스 내 모든 멤버에 접근할 수 있으면서 인스턴스 생성 없이 호출할 수 있는 함수를 작성할 수 있다.
- 어디서든 접근이 가능하다.
- 즉, 자바의 static 메소드, static 멤버와 같은 역할을 한다고 볼 수 있다.
- 왜냐하면 코틀린은 클래스 내에 정적 필드나 정적 함수를 둘 수 없다. 그러한 개념이 없기 때문이다. 대신에 클래스별로 하나씩 클래스의 인스턴스 생성 없이 사용할 수 있는 오브젝트를 정의할 수 있는데, 이를 동반 객체라고 한다.
싱글톤
- 단 하나의 인스턴스만 생성되도록 제약을 둔 패턴으로 코틀린에서 object를 사용해 간편하게 선언할 수 있다.
1 | object Singleton{ |
코틀린에서의 특징만 살펴보도록 하겠다. 다 정리하기에는 시간이 오래 걸릴 듯 싶어서이다…
시간이 생긴다면 정리하지 않은 특징도 추구하도록 하겠다. :)
is 연산자
- 자료형을 확인하기 위해 사용한다.
- 자바의 instanceOf 연산자와 동일한 기능을 한다.
- 특정 타입이 아닌 경우를 확인하기 위해서는
!is
로 확인하면 된다.
1 | fun Test(obj : Any){ |
as 연산자
- 특정 변수를 원하는 자료형으로 변환하기 위한 연산자이다.
1 | fun porcessNumber(number: Number){ |
스마트 캐스트
- 자료형 추론이 가능할 경우 캐스팅 없이 해당하는 자료형으로 객체를 사용할 수 있도록 하는 기능이다.
- 값을 검사하는 시점과 사용하는 시점 사이에 값이 변하지 않았다는 것이 보장되는 경우에만 지원된다. 따라서 언제든지 값이 변할수 있는 var는 스마트 캐스트가 지원되지 않는다.
범위
- 특정 범위를 순회하거나 해당 범위 내에 특정 항목이 포함되어 있는지 확인할 때 사용한다.
- … 연산자를 사용.
1 | // 0부터 10까지, 시작과 끝을 포함하는 범위를 정의한다. |
- 인덱스 순환을 위한 범위를 생성할 때는 until 함수를 사용하면 된다.
- 가장 마지막 값을 포함하지 않는다.
- 범위 내에 특정 항목이 있는지 확인할 때는 in 연산자를 사용한다.
1 | // 0<=N<10 |
downTo()
함수를 이용해 항목들의 순서가 반대로 정렬된 범위를 생성한다.- 시작과 끝을 포함한다.
- 기본적으로 1씩 감소 시킨다.
step()
함수를 사용하여 감소/증가 폭을 변경할 수 있다.
1 | for(i in 5 downTo 1){ |
예외
- 사용법은 자바와 동일하다. 하지만, 코틀린에서는 값을 반환할 수 있다.
checked exception
(뭐지? 검색해보자.)을 따로 검사하지 않는다. 즉, 대부분의 예외를 try-catch문으로 감싸 처리해야 했던 자바와 달리 코틀린에서는 이를 선택적으로 사용할 수 있다.
1 | fun readFromJson(fileName : String) : String{ |
# 널안정성
코틀린은 컴파일 단계에서 발생하는 널 포인터 예외가 발생할 문제를 해결하기위해 모든 타입에 명시적으로 널 허용 여부를 함께 표기한다.
코틀린의 중요한 특징 중 하나라고 생각한다.
널 허용 여부 표기
- 널 값을 가질 수 있도록 하려면 명시적으로 타입 뒤에 ? 를 붙여주어야 한다.
- 널 값을 허용하지 않는 변수를 초기화하지 않거나, null을 대입하면 컴파일 오류를 발생시킨다.
- 함수의 파라미터나 반환 값에도 동일하게 적용된다.
1 | val a : String? = null |
엘비스(?:) 연산자
- 널 값을 허용하지 않는 값 혹은 변수에 널 값을 반환할 수 있는 함수의 결과를 대입해야 하는 경우 엘비스 연산자를 이용해 편리하게 처리할 수 있다.
- 널 값을 대신하는 방법으로
?:
를 사용한다. - 널 여부를 확인하는 작업을 별도로 하지 않아도 되므로 자바에 비해서 간편하게 함수의 로직을 작성할 수 있다.
- 값 반환 대신 예외를 발생시킬 수도 있다.
1 | foo ?: bar |
안전한 호출(?.) 연산자
- 널 값 확인을 위해 자바에서 if문으로 검사하던 것을 간편화 할 수 있다.
- 간단한 로직의 경우 상관이 없지만 복잡해질수록 if문의 향연이 펼쳐질 수 있다.
- 안전한 호출 연산자를 사용하여 널 값 확인과 값 접근/함수 호출을 한 번에 할 수 있다.
- 이 연산자를 사용하는 객체가 널 값이 아닌 경우에만 연산자 뒤의 문장을 수행한다. 널 값일 경우에는 뒤의 문장을 수행하지 않고 null 값을 반환한다.
- 따라서 널 값인 객체의 프로퍼티를 참조하거나 함수를 호출하는 일을 방지할 수 있다.
1 | // bar가 null이 아닐 경우에만 해당 값인 baz를 대입. 그렇지 않은 경우 null을 foo에 대입 |
as? 연산자
- 자바에서 지원되지 않는 자료형으로 변환을 시도할 가능성이 있는 부분을 try-catch 블록으로 감싸서 처리한다.
- 코틀린에서는
as?
연산자를 사용해 간편하게 해결할 수 있다. - 자료형 변환이 실패할 경우 예외를 발생시키는 대신 널 값을 반환한다.
1 | val foo : String = "foo" |
- 엘비스 연산자를 사용해 변환에 실패했을 때의 기본값을 지정할 수 있다.
- 변환된 값을 받은 자료형의 널 허용 여부를 수정할 필요가 없으므로 유연하게 대처할 수 있다.
1 | // 자료형 변환에 실패하는 경우 기본 값을 0으로 지정. |
널 값이 아님을 명시하기 : 비 널 값 보증
- 널 값을 포함할 수 있는 타입을 널 값을 포함하지 않는 타입으로 변환하여 사용할 수 있다.
- 보증하려는 항목 뒤에 !!을 붙여 사용한다.
- 비 널 값 보증을 사용했지만 실제로 객체나 값에 널 값이 들어가 있을 경우 널 포인ㅌ 예외가 발생하므로 유의해서 사용해야 한다.
1 | // 값 foo는 널 값을 포함할 수 있는 타입. |
비 널 값 보증 사용 주의
비 널 값 보증은 아래처럼 중첩해서 사용하는 것을 권장하지 않는다.
contact.address와 address.line2 중 하나라도 널 값이라면 널 포인터 예외가 발생한다.
하지만 에러 로그에서 라인만 알 수 있을 뿐, 어느 요소로 예외가 발생했는지 알 수 없다.
따라서 비 널 값 보증은 중첩되는 호출 단계보다는 하나의 호출 단계에만 사용할 것을 권장한다.
1 | val contact : Contact = ... |
lateinit 키워드
- 초기화 없이 변수만 선언할 수 있다. (var에만 사용할 수 있다.)
- 즉, 초기화를 나중에 한다는 의미이다.
- 초기화를 하지 않은 채로 사용하려고 하면 널 포인터 예외가 발생하기 때문에 초기화 작업을 반드시 해야 한다.
- 초기화가 되었는지 확인하는 방법이 있다.
1 | class MainActivity: Activity(){ |
주의!
코틀린에서는 값을 반환하는 구문들이 있다.
for, while, when, try-catch, if 등등이 있다.
이러한 구문들이 값을 반환한다는 특징을 알고 코드를 더 편리하게 짤 수 있으므로 알아두면 좋을 것 같다.