Skip to content

Latest commit

 

History

History
336 lines (240 loc) · 11 KB

코틀린-간단한 개념2.md

File metadata and controls

336 lines (240 loc) · 11 KB

들어가기에 앞서

class Test{
	// 생성자가 없는 기본 클래스의 형태.
}

class Test(){
	// 인자가 없는 생성자가 있는 클래스의 형태.
}

생성자

  • 자바와 조금 다르다
  • init 블록을 사용해 기본 생성자를 대체할 수 있다.
  • 생성자에 인자가 필요한 경우 인자처럼 받을 수 있다. → 이를 주 생성자라 부른다.
class Test{
	
	init{
		// 기본 생성자에서 수행할 작업.
		// 예를 들면, 초기화?!
	}
}

class Test(a: Int){
	
	init{
		println(a)
	}
}
  • 생성자의 인자를 통해 클래스 내부의 프로퍼티에 값을 할당한다.
  • 생성자의 인자를 통해 프로퍼티 선언을 대신할 수 있다. 따라서 추가로 프로퍼티 선언이 필요 없다.
  • 생성자의 인자에서 프로퍼티 선언이 함께 이루어지고, 값 할당 또한 생성자 호출과 동시에 수행되므로 짧은 코드를 확인할 수 있다.
class Test(val a: Int, val b: Int){
	// 생략..
}
  • 주 생성자 외에 다른 형태의 생성자가 필요한 경우 constructor 키워드를 사용해 선언할 수 있다.
class Test(val a: Int, val b: Int){

	// a 값만 인자로 받는 추가 생성자.
	// 기본 생성자를 반드시 호출해야 한다.
	constructor(a: Int) : this(a,0)

	// 두 인자의 값을 모두 0으로 지정하는 생성자.
	constructor() : this(0,0)
  • 추가 생성자를 정의하는 경우 주 생성자를 반드시 호출해야 한다.(상속과 this 키워드를 이용해서!) 즉, 추가 생성자는 주(기본) 생성자를 상속받아야 한다.
  • 추가 생성자는 인자와 프로퍼티를 함께 선언할 수 없다. 따라서 프로퍼티 선언이 필요한 경우 주 생성자에서 이를 처리해야 한다.
  • 생성자 앞에 접근 제한자를 붙여 가시성을 변경할 수 있다.
class Test private constructor(val a: Int, val b: Char){

	private constructor(a: Int): this(a,0)

	constructor(): this(0,0)

함수

  • 메소드를 함수로 표현한다.
  • 반환 값이 없을 경우 Unit을 사용하며, 생략 가능하다.

상속 및 인터페이스

  • 클래스의 상속, 인터페이스의 구현 모두 콜론(:)을 사용한다.
  • 클래스를 상속받는 경우 반드시 부모 클래스의 생성자를 호출해야 하며, 부모 클래스의 생성자는 super 키워드를 사용해 호출한다.
class MyView: View{
	constructor(context: Context) : super(context){
	
	}

	constructor(context: Context, attrs: AttributeSet?): super(contenxt, attrs){
		// 뷰 초기화.
	}

	// 생성자가 여럿일 경우, this 키워드를 사용해 자기 자신의 생성자를 호출할 수 있다.
	constructor(context: Context) : this(context, null){
	
	}
  • 오버라이드는 어노테이션 대신 override 키워드를 사용.

  • open

    키워드를 사용해 클래스 혹은 함수의 상속 여부를 결정한다.

    • open 키워드가 있으면 상속 가능.
    • open 키워드가 없으면 상속 불가능.(자바의 final 키워드와 비슷하다.)

this

  • this 키워드를 사용한 클래스 자신을 지칭할 때 사용한다.
  • 해당 위치에서 가장 가까운 범위의 클래스를 의미한다. 따라서 클래스 내에서 다른 클래스나 인터페이스의 객체를 동적으로 생성하여 사용하는 경우, 키워드를 사용하는 위치에 따라 this가 의미하는 클래스가 달라질 수 있다.
  • 따라서 this@{클래스 이름} 처럼 명확하게 표현해주는 것이 좋다.

동반 객체(companion object)

  • 코틀린은 클래스 내에 정적 필드나 정적 함수를 가질 수 없다. 이러한 개념이 없기 때문이다. 대신 클래스 별로 하나씩 클래스의 인스턴스 생성 없이 사용할 수 있는 오브젝트를 정의할 수 있으며, 이를 동반 객체라고 한다.
  • 동반 객체를 사용하면 클래스 내 모든 멤버에서 접근할 수 있으며, 인스턴스 생성 없이 호출할 수 있는 함수를 작성할 수 있다.
  • 어디서든 접근이 가능하다.
  • 즉, 자바의 static 메소드, static 멤버와 같은 역할을 한다고 볼 수 있다.

싱글톤

  • 단 하나의 인스턴스만 생성되도록 제약을 두는 디자인 패턴으로 코틀린에서는 object 키워드를 사용해 간편하게 선언할 수 있다.
object Singleton{
 val a = "a"

 fun aa(){
  println(a)
  }
}

val value = Singleton.a
Singleton.aa()

is 연산자

  • 자료형을 확인하기 위해 사용한다.
  • 특정 타입이 아닌 경우를 확인하기 위해서는 !is 로 확인하면 된다.
fun Test(obj: Any){
	if(obj is Int){
		println("Int 입니다.")
	}else if(obj is Float){
		println("Float 입니다.")
	}
	
	...
}

as 연산자

  • 특정 변수를 원하는 자료형으로 변환하는데 사용한다.
fun processNumber(nunber: Number){
	val value : Int = number as Int
}

범위

  • 특정 범위를 순회하거나 해당 범위 내에 특정 항목이 포함되어 있는지 확인할 때 사용한다.

  • '..' 연산자 사용.

    • 처음과 끝을 모두 포함한다.
  • 인덱스 순환을 위한 범위를 생성할 때는 until 함수를 사용한다.

  • 가장 마지막 값을 포함하지 않는다.

  • 범위 내에 특정 항목이 있는지 확인할 때는 in 연산자를 사용한다.

val range: IntRange = 0..10
// 0<=n<=10

val range2: IntRange = 0 until 10
// 0<=n<10

val value: Boolean = 5 in range
// 5가 range 내에 포함되어 있는지 확인.
// true 반환.

val value2: Boolean = 5 !in range2
// 5가 range2 내에 포함되어 있지 않는지 확인.
// false 반환.
  • downTo()

    함수를 사용해 항목들의 순서가 반대로 정렬된 범위를 생성한다.

    • 시작과 끝을 포함한다.
    • 기본적으로 1씩 감소시킨다.
  • step() 함수를 사용하여 감소/증가 폭을 변경할 수 있다.

for(i in 5 downTo 1){
	print(i)
}
// 결과 54321

for(i in 5 downTo 1 step 2){
	print(i)
}
// 결과 531

예외

  • 값을 반환할 수 있다.
  • checked exception을 따로 검사하지 않는다. 즉, 대부분의 예외를 try-catch 문으로 감싸 처리해야 했던 자바와 달리 코틀린에서는 이를 선택적으로 사용할 수 있다.

널 안전성

컴파일 단계에서 널 포인터 예외가 발생할 문제를 해결하기 위해 모든 타입에 명시적으로 널 허용 여부를 함께 표기하도록 한다.

널 허용 여부 표기

  • 명시적으로 타입 뒤에 ? 를 붙여 널 값을 가질 수 있도록 한다.
  • 널 값을 허용하지 않는 변수를 초기화하지 않거나, null을 대입할 경우 컴파일 에러 발생.
  • 함수의 파라미터나 반환 값 역시 동일하게 적용된다.
val a: String? = null
val b: String = "b"

// 아래 두 경우 컴파일 에러 발생.

val name: String
// 초기화되지 않음.
val address: String = null
// null을 허용하지 않음.

엘비스 연산자

  • 널 값을 허용하지 않는 값 혹은 변수에 널 값을 반환할 수 있는 함수의 결과를 대입해야 하는 경우에 사용할 수 있다.
  • 널 값을 대신하는 방법으로 ?: 를 사용한다.
  • 널 여부를 확인하는 작업이 필요 없으므로 간편하게 쓸 수 있다.
  • 값 반환 대신 예외를 발생시킬 수도 있다.
val value = foo ?: 0
// foo가 null일 때는 0을 반환.
// foo가 null이 아닐 때는 foo를 반환.

안전한 호출 연산자

  • 널 값 확인을 위해 if문 사용을 하던 자바와 다르게 간편하게 처리 가능.
  • **안전한 호출 연산자(?.)**를 사용하여 널 값 확인과 값 접근, 함수 호출을 한 번에 할 수 있다.
  • ?. 를 사용하는 객체가 널이 아닌 경우에만 연산자 뒤의 문장을 수행한다. 널일 경우에는 뒤의 문장을 수행하지 않고 null 값을 반환한다.
  • 따라서 널 값인 객체의 프로퍼티를 참조하거나 함수를 호출하는 일을 방지할 수 있다.
val foo = bar?.baz
// bar가 널이 아닐 경우에만 해당 값인 baz를 대입.
// 그렇지 않은 경우 null을 foo에 대입.

foo?.bar()
// foo가 null이 아닐 경우에만 bar() 함수 호출.

as? 연산자

  • 자바에서 지원되지 않는 자료형으로 변환을 시도할 가능성이 있는 부분을 try-catch 블록으로 감싸서 처리한다.
  • 코틀린에서는 as? 연산자를 사용해 간편하게 처리한다.
  • 자료형 변환이 실패할 경우 예외 발생 대신 널 값을 반환한다.
val foo: String = "foo"

val bar: Int? = foo as? Int
// 자료형 변환에 실패하므로 bar로 null 값이 할당된다.
  • 위에서 본 엘비스 연산자를 사용해 변환에 실패했을 때의 기본값을 지정할 수 있다.
  • 변환된 값을 받은 자료형의 널 허용 여부를 수정할 필요가 없으므로 유연하게 대처할 수 있다.
val bar: Int? = foo as? Int?:0
// Int로의 자료형 변환에 실패하는 경우에 null 값을 반환하는데,
// 실패해서 null 값을 반환하면 즉, null이라면 0을 반환하도록 엘비스 연산자를 통해 기본값 설정을 한다.

널 값이 아님을 명시하기: 비 널 값 보증

  • 널 값을 포함할 수 있는 타입을 널 값을 포함하지 않는 타입으로 변환하여 사용할 수 있다.
  • 보증하려는 항목 뒤에 !! 를 붙여 사용한다.
  • 비 널 값 보증을 사용했지만 실제로 객체나 값에 널 값이 들어가 있을 경우 NPE가 발생하므로 유의해서 사용해야 한다.
val foo: Foo?=...
// 값 foo는 null 값을 포함할 수 있다.

val nonNullFoo: Foo = foo!!
// foo는 널 값을 포함하지 않음을 보증.

foo!!.bar()
// 값 foo가 널 값이 아님을 보장하면서 bar() 함수 호출.
  • 비 널 값 보증은 중첩되는 호출 단계보다는 하나의 호출 단계에서만 사용할 것을 권장한다.

lateinit 키워드

  • 초기화 없이 변수(var)만 선언할 수 있다.
  • 즉, 초기화를 나중에 한다는 의미이다.
  • 사용 전에 반드시 초기화를 해야 한다.
  • 초기화를 하지 않은 채로 사용하려고 하면 NPE 예외가 발생하기 때문에 초기화 작업은 필수적이다.
  • 초기화가 되었는지 확인하는 방법이 존재한다.
class Test{

	lateinit var api: API

	fun process(){
		if(::api.isInitialized){
			// 작업 수행. 초기화 됨.
		}else{
			// 작업 수행. 초기화 안됨.
		}
	}
}

기타

  • 코틀린에서는 값을 반환하는 구문들이 있다.
  • for, while, when, try-catch, if 등이 있다. 이 구문들이 값을 반환하는 특징을 갖고 있으므로 이를 활용해 코드를 더 간결하게 짤 수 있다.