Kotlin interview questions
paulfranco
https://gist.github.com/paulfranco/4453383cc6df064d03087ce7aa5a0c8c
Answer
primary 생성자는 클래스 헤더의 일부분이다.
Java 와 달리 클래스 body 에 정의할 필요 없다.
class Person(val firstName: String, var age: Int) {
// class body
}
Answer
data 를 앞에 붙인, 데이터를 가지기위한 클래스
data class User(val name: String, val age: Int)
최소 1개 이상의 parameter 가 있어야 한다.
val 또는 var parameter 이어야 한다.
abstract, open, sealed, inner 는 안된다.
Answer
Kotlin type system 은 null 참조의 위험성을 제거하는 것을 목표로 한다.
Java 를 포함한 많은 프로그래밍 언어에서 가장 일반적인 함정 중 하나는 null 참조를 하면 예외가 발생한다는 것이다.
Java 에서는 NPE(NullPointerException) 이라 한다.
Kotlin 에서는 null 을 보유할 수 있는 참조와 아닌 참조로 나눈다.
var a: String = "abc"
a = null // compilation error
var b: String? = "abc"
b = null // ok
Answer
Extension 은 실제로 클래스를 수정하지 않습니다.
점 표기법(dot-notation) 을 이용해 새 함수를 호출할 수 있게 만듭니다.
open class BaseClass
class DerivedClass : BaseClass()
fun BaseClass.someMethod(){
print("BaseClass.someMethod")
}
fun DerivedClass.someMethod(){
print("DerivedClass.someMethod")
}
fun printMessage(base : BaseClass){
base.someMethod()
}
printMessage(DerivedClass()) // BaseClass.someMethod
printMessage(DerivedClass()) 를 통해 출력될 메세지는 "BaseClass.someMethod" 이다.
fun printMessage(base : BaseClass){
base.someMethod()
}
base.someMethod() 확장 함수는 호출되는 printMessage 함수의 base 매개변수에 선언된 타입에만 의존하기 때문입니다.
런타임에 동적으로 타입이 정해지는게 아니라 정적으로 정해집니다.
Answer
Kotlin 은 static members 나 member 함수가 없다.
만약 쓰고 싶다면 companion object 로 클래스 내부에 선언하면 된다.
class EventManager {
companion object FirebaseManager { }
}
val firebaseManager = EventManager.FirebaseManager
companion object 는 싱글톤입니다.
Java 코드에서 호출이 필요한 경우 @JvmStatic 을 이용할 수 있습니다.
Answer
lateinit 키워드로 변수를 선언하면, 초기화를 나중에 할 수 있습니다.
초기화가 될때까지 메모리에 할당하지 않습니다.
Int, Long 과 같이 primitive 한 타입에는 쓸 수 없습니다.
lateinit var test: String
fun doSomething() {
test = "Some value"
println("Length of string is "+test.length)
test = "change value"
}
Answer
코드에서 해당 변수를 이용하지 않는 한, 변수는 초기화되지 않습니다.
한 번만 초기화되고 항상 같은 값만 가지고 있습니다.
lazy() 함수는 람다를 통해 lazy 인스턴스를 반환받습니다.
첫번째 get() 호출은 lazy() 람다를 실행해 결과를 저장하고, 이후 get() 호출은 단순히 저장된 결과를 반환만 합니다.
val test: String by lazy {
val testString = "some value"
}
Answer
속성이 mutable 일 경우 lateInit 이용
속성이 외부에서 설정된 경우(설정하기 위해 외부 변수를 전달해야 하는 경우) lateInit 이용
한 번만 초기화되고 모두가 공유하도록 되어 있고, 내부적으로 설정되어 있으면 lazy initialization 이용
lateInit
모든 위치에서 초기화할 수 있다.
다중 초기화가 가능하다.
Non-thread safe
var 만 이용 가능하다.
값 초기화 여부를 확인하기 위해 isInitialized 함수 존재 함
primitive 타입 속성 비허용
lazy initialization
initializer 람다에서만 초기화 할 수 있다.
한 번만 초기화할 수 있다.
기본적으로 스레드로부터 안전하며, 초기화가 한 번만 호출되도록 보장합니다.
val 만 이용 가능하다.
초기화를 취소할 수 없습니다.
primitive 타입 속성에 허용
Answer
var 는 일반 변수와 같으며 mutable 변수로, 여러 번 할당할 수 있습니다.
val 는 Final 변수와 같으며 immutable 변수로, 한 번만 초기화할 수 있습니다.
class Student (var name: String) {
init() {
println("Student has got a name as $name")
}
constructor(sectionName: String, var id: Int) this(sectionName) { }
}
Answer
클래스의 property 를 secondary 생성자에 선언할 수 없습니다.
class Student (var name: String) {
var id: Int = -1
init() {
println("Student has got a name as $name")
}
constructor(secname: String, id: Int) this(secname) {
this.id = id
}
}
val aVar by lazy {
println("I am computing this value")
"Hola"
}
fun main(args: Array<String>) {
println(aVar)
println(aVar)
}
Answer
I am computing this value
Hola
Hola
Answer
open 키워드는 "open for extension" 을 의미합니다.
Java 의 final 과 반대입니다.
open 키워드를 통해 해당 클래스를 상속할 수 있습니다.
visibility modifier 를 지정하지 않으면 기본적으로 public 입니다.
Answer
const 는 컴파일 타임 상수입니다.
런타임에 할당받는 val 와 달리, 컴파일 타임 동안 값을 할당 받습니다.
따라서 const 는 함수나 클래스 생성자에 할당할 수 없고, String 또는 primitive 타입만 할당할 수 있습니다.
Answer
Array<Int> => Integer[]
IntArray => int[]
서로 다른 타입이기 때문에 같이 쓸 수 없다.
Answer
object 는 생성자가 없기 때문에 parameter 전달이 불가능하다.
아래처럼 전달할 수 있다.
class UsersDatabase : RoomDatabase() {
companion object {
@Volatile private var INSTANCE: UsersDatabase? = null
fun getInstance(context: Context): UsersDatabase =
INSTANCE ?: synchronized(this) {
INSTANCE ?: buildDatabase(context).also { INSTANCE = it }
}
private fun buildDatabase(context: Context) = Room.databaseBuilder(context.applicationContext, UsersDatabase::class.java, "Sample.db")
.build()
}
}
Answer
Primivite 타입에 대한 inline 을 제공한다.
단일 property 가 있는 기본 생성자를 가진 클래스 앞에 inline 수정자를 붙이면, 아래와 같이 컴파일 시 값으로 대체됩니다.
함수는 정적 함수가 됩니다.
inline class Name(private val value: String) {
fun greet() {
// ...
}
}
// Code
val name: Name = Name("Marcin")
name.greet()
// During compilation replaced with code similar to:
val name: String = "Marcin"
Name.`greet-impl`(name)
primary 생성자에서 초기화된 단일 property 는 필수입니다.
Init block, inner class, backing field 는 허용하지 않습니다.
interface 만 상속할 수 있습니다.
final 과 같은 효과입니다.
Answer
type aliases 는 동일한 타입을 가지고 있으면 호환이 되고,
inline 클래스는 호환이 안됩니다.
typealias NameTypeAlias = String
inline class NameInlineClass(val s: String)
fun acceptString(s: String) {}
fun acceptNameTypeAlias(n: NameTypeAlias) {}
fun acceptNameInlineClass(p: NameInlineClass) {}
fun main() {
val nameAlias: NameTypeAlias = ""
val nameInlineClass: NameInlineClass = NameInlineClass("")
val string: String = ""
// OK: pass alias instead of underlying type
acceptString(nameAlias)
// Not OK: can't pass inline class instead of underlying type
acceptString(nameInlineClass)
// OK: pass underlying type instead of alias
acceptNameTypeAlias(string)
// Not OK: can't pass underlying type instead of inline class
acceptNameInlineClass(string)
}
Answer
Backing field 는 접근자(getter/setter) 내에서만 이용할 수 있는 자동 생성된 필드입니다.
var selectedColor: Int = someDefaultValue
get() = field
set(value) {
field = value
}
Answer
fun <T> myGenericFun(c: Class<T>)
myGenericFun 함수에서는 T 타입을 알 수 없습니다.
Java 에서와 같이 런타임에 지워지고 컴파일 시 이용할 수 있기 때문입니다.
inline fun <reified T : Activity> Activity.startActivity() {
startActivity(Intent(this, T::class.java))
}
inline 함수와 함께 reified 키워드를 쓰면, T 타입을 알 수 있습니다.
이러한 함수는 컴파일러가 함수의 바이트코드를 함수가 이용되는 모든 위치에 복사합니다.
따라서 컴파일러는 reified 타입의 실제 타입을 알고 있습니다.