brunch

You can make anything
by writing

C.S.Lewis

by 이승현 Jan 28. 2022

Kotlin 면접 질문 #02

Kotlin interview questions

InterviewBit


https://www.interviewbit.com/kotlin-interview-questions/#freshers




Question #01

How are variables declared in Kotlin?

(Kotlin 에서 변수 선언 방법은?)


Answer


Immutable variables

read-only 변수라고도 알려져 있다.

val 키워드를 써서 선언해야하고, 한번 선언되면 값을 변경할 수 없다.


val sample = "interview"
sampe = "interviewBit"        // compile error


Immutable 변수는 constant(상수) 가 아니다, 상수가 아니기 때문에 compile-time 에 값을 알 필요가 없다.

아래 코드처럼 호출할때마다 값이 바뀔 수 있다.


var sample = "interview"
val newSample = sample    // no compile error


Mutable variables

값을 변경할 수 있다.

var 키워드를 써서 선언한다.


var sample = "interview"
sampe = "interviewBit"




Question #02

What are data classes in Kotlin?

(data 클래스란?)


Answer


Data 클래스는 data 를 보유하고 일반적인 함수들을 제공하는 클래스입니다.


data class className ( list_of_parameters)


아래와 같은 함수들을 제공하고 있습니다.

equals()

hashCode()

copy()

toString()


그리고 다음과 같은 사항들이 있습니다.


적어도 하나 이상의 매개변수가 primary 생성자에 있어야 한다.

Abstract, open, sealed, inner 는 허용되지 않습니다.

오직 interface 만 data 클래스에 의해 구현될 수 있습니다.




Question #03

Explain the concept of null safety in Kotlin.

(null safety 란?)


Answer


Kotlin type system 은 null(nullable references) 을 보유할 수 있는 reference 와 그렇지 않은 reference(non-null references) 로 구분합니다.


nullable references 는 타입뒤에 ? 를 붙여서 선언할 수 있습니다.

var a: String = "interview" 
a = null     // results in compilation error

var b: String? = "interview"
b = null     // no compilation error


Kotlin 은 null 이 발생한 경우 이용할 수 있는 연산자들을 제공합니다.

Safe Call : ?.

Elvis : ?:

Not Null Assertion : !!


Kotlin 은 nullable references 와 non-null references, 그리고 다양한 연산자들을 이용해서 null 을 안전하게 처리할 수 있습니다.




Question #04

Explain Safe call, Elvis and Not Null Assertion operator in the context of Kotlin.

(Safe call, Elvis, Not Null Assertion 연산자란?)


Answer


Safe Call operator ( ?. )


Kotlin 에서는 특정 reference 가 null 값이 아닐 경우에만 작업을 수행하는 작업을 단순화하는 Safe Call 연산자(?.) 가 있습니다.



name?.toLowerCase()        // Safe Call operator


// 위 Safe Call operator 는 아래와 같습니다.

if(name != null)     
    name.toLowerCase()
else    
    null 


Elvis Operator ( ?: ) 


Elvis 연산자의 왼쪽 표현식이 null 이 아니면 왼쪽 표현식을 반환하고,

왼쪽 표현식이 null 이면 오른쪽 표현식을 반환합니다.



val sample1 = sample2 ?: "Undefined"        // Elvis operator

val sample1 = if (sample2 != null)         
        sample2      
    else         
        "Undefined"

// 아래와 같이 예외를 throw 할 수도 있습니다.
val sample1 = sample2 ?: throw IllegalArgumentException("Invalid")


Not Null Assertion Operator ( !! )


값이 null 이면, 예외를 throw 합니다.

NullPointerException 을 원하면 이 연산자를 사용하여 명시적으로 요청할 수 있습니다.


fun main(args: Array<String>) {    
    var sample : String?  = null    
    str!!.length        // Exception in thread "main" kotlin.KotlinNullPointerException
}




Question #05

Differentiate between Kotlin and Java.

(Kotlin 와 Java 차이점 비교)


Answer


1. Null Safety

Kotlin

기본적으로 Kotlin 의 모든 종류의 변수는 null 값을 허용하지 않습니다.

만약 null 값을 허용하려면 아래와 같이 선언할 수 있다.

value num: Int? = null


Java

Java 에서는 NullPointerExceptions 는 큰 골칫거리입니다.

모든 변수에 null 을 할당할 수 있기 때문에, null 값을 이용하는 개체를 참조할때 NPE 가 발생하는지를 개발자가 직접 관리해야 합니다.




2. Coroutines Support

Kotlin

Kotlin 은 스레드를 차단(block) 하지 않고, 실행을 중지(suspend) 할 수 있는 코루틴을 지원합니다.


Java

Java 는 수많은 스레드를 생성하고 실행할 수 있지만, 이를 관리하는 것은 쉽지 않습니다.




3. Data classes

Kotlin

Kotlin 은 data 를 보유하는 클래스가 필요한 경우, data 키워드를 클래스에 정의할 수 있습니다.

컴파일러에 의해 생성자 매개변수에 자동으로 getter/setter 를 처리하고, 다양한 기본 함수들도 제공합니다.


Java

Java 는 data 를 보유하는 클래스가 필요한 경우, data 를 저장할 변수, getter/setter, 그리고 기본 함수들도 개발자가 직접 명시적으로 작성해야 합니다.




4. Functional Programming

Kotlin

Kotlin 은 lambda expressions, operator overloading, higher-order functions, and lazy evaluation 등 다양한 기능을 가진 절차형(procedural), 함수형(functional) 프로그래밍 언어입니다.


Java

Java 8 까지 함수형 프로그래밍을 지원하지 않지만, Android 앱을 개발할 때 Java 8 의 하위 집합으로 지원합니다.




5. Extension Functions

Kotlin

기존 클래스에 새로운 기능을 추가할 수 있습니다.

fun MutableList<Int>.swap(index1: Int, index2: Int) {     
    val tmp = this[index1]
    this[index1] = this[index2]     
    this[index2] = tmp
}


val list = mutableListOf(1, 2, 3)
list.swap(0, 2)


Java

Java 에서는 기존 클래스에 새로운 기능을 추가하려면, 상속을 통해 구현해야 합니다.

따라서 Java 는 기본적으로 Extension 함수가 없습니다.




6. Data Type Inference 

Kotlin

변수의 Type 을 명시적으로 지정하지 않아도 괜찮습니다.

val str = "String" // 타입 추론


Java

Java 에서는 변수의 Type 을 명시적으로 선언해야 합니다.




7. Smart Casting 

Kotlin

변수의 Type 을 직접 검사하지 않고, 컴파일러가 알아서 검사하고 캐스팅 합니다.

fun smartCast(ant : Any) {
     when (any) {
          is Int -> println(any + 2)
          is String -> println(any)
         else -> return
    }
}


Java

Java 에서는 변수의 Type 을 검사하고 적절하게 casting 해야 합니다.




8. Checked Exceptions

Kotlin

Kotlin 에서는 checked exceptions 이 없습니다.

따라서 개발자가 선언하거나 catch 할 필요가 없습니다.


Java

Java 에서는 checked exceptions 을 지원하며, 이를 개발자가 직접 선언하고 catch 해야 합니다.




Question #06

What are the different types of constructors available in Kotlin?

(생성자 유형은? )


Answer


Primary Constructor

class header 에 초기화되며, class 이름 뒤에 선언합니다.

constructor 키워드를 이용해서 선언합니다.

class Sample constructor(val a: Int, val b: Int) {
    // code
 }


primary 생성자는 코드를 포함할 수 없기 때문에, 초기화 코드는 init block 에서 처리해야 합니다.

init block 은 객체가 생성된 후 호출됩니다.

class Sample (a : Int , b: Int) {
     val p: Int    
    var q: Int    

    // initializer block    
    init {       
        p = a        
        q = b        
        println("The first parameter value is : $p")        
        println("The second parameter value is : $q")    
    }
 }


Secondary Constructor


Secondary 생성자를 이용하면 변수를 초기화하고 logic 을 추가할 수 있습니다.

constructor 키워드를 이용해서 선언합니다.

class Sample {    
    constructor(a: Int, b: Int) {        
        println("The first parameter value is : $p")        
        println("The second parameter value is : $q")    
    }
}


Kotlin 에서는 하나의 primary 생성자와 하나 이상의 secondary 생성자를 가질 수 있습니다.

primary 생성자는 클래스를 초기화하는 반면, secondary 생성자는 초기화와 몇 가지 logic 을 추가할 수 있습니다.




Question #07

How can you concatenate two strings in Kotlin?

(두 string 을 연결할 방법은? )


Answer


val s1 = "Interview"
val s2 = "Bit"

val s3 = "$s1 $s2"             // Using String Interpolation

val s4 = s1 + s2                 // Using the +
val s5 = s1.plus(s2)          // Using the plus() operator

val s6 =  StringBuilder()      
s6.append(s1).append(s2) 
val s7 = s6.toString()        // Using StringBuilder




Question #08

What do you understand about Companion Object in the context of Kotlin?

(Companion Object? )


Answer


Java 와 같은 일부 언어에서는 static 키워드를 이용하여 class member 를 선언하고, object 생성 없이 이를 활용합니다.


Kotlin 에서는 static 키워드가 없습니다.

companion object 를 이용해서 구현할 수 있습니다.


class CompanionClass {     
    companion object CompanionObjectName {       
        // code     
    }
}

val obj = CompanionClass.CompanionObjectName


// 이름 없이 선언할 수 도 있습니다.
class CompanionClass {    
    companion object {      
        // code    
    }
}

val obj = CompanionClass.Companion


class Sample {    
    companion object Test {
        // companion object 내부에 변수와 함수를 선언할 수 있습니다.
        var a: Int = 1        
        fun testFunction() = println("Companion Object’s Member function called.")    
    }
}

fun main(args: Array<String>) {    
    println(Sample.a)    
    Sample.testFunction()
}




Question #09

Differentiate between open and public keywords in Kotlin.

(open 과 public 키워드의 차이는? )


Answer


open 키워드는 확장(expansion) 을 용어를 나타냅니다.

Java 에서 쓰이는 final 과 반대 개념입니다.


Kotlin 에서는 기본적으로 클래스를 상속할 수 없습니다.

open 키워드를 이용하면, 상속을 지원합니다.


Kotlin에서는 기본적으로 모든 클래스가 public 합니다.

visibility modifier 가 지정되지 않으면 기본적으로 public 입니다.




Question #10

Explain about the “when” keyword in the context of Kotlin.

(when 키워드? )


Answer


Java 의 switch 문 대신 when 을 이용합니다.


fun main(args: Array<String>) {        
    var temp = "Interview"    
    
    when(temp) {        
        "Interview" -> println("Interview Bit is the solution.")        
        "Job" -> println("Interview is the solution.")        
        "Success" -> println("Hard Work is the solution.")    
    }
}




Question #11

Which one is better to use - val mutableList or var immutableList in the context of Kotlin?

(val mutableList 와 var immutableList 중 더 좋은것은? )


Answer


Collection 의 이용 목적에 따라 나눌 수 있다.

Collection 이 변경될 경우 mutable list 를 쓰고, 반대로 view 만 한다면 immutable list 를 씁니다.


val 와 var 는 immutable list 와 mutable list 와는 다른 이용 목적을 가지고 있습니다.

val 는 변수의 값(value)/참조(reference) 가 한 번만 할당되고 이를 실행중 수정할 수 없을때 이용 합니다.

반대로 var 는 변수의 값/참조가 언제든 변경될 수 있을때 이용합니다.


Immutable list 는 아래와 같은 장점들이 있습니다.

immutable 이기 때문에 상태(state) 를 변경하지 않습니다.
따라서 다음 함수(ex. map, filter, reduce ...)로 전달할 때 상태 변경 없이, 새로운 상태를 구성해 전달하는 함수형 프로그래밍 기법을 추구합니다.

side effect 가 없기 때문에 이를 이해하고 디버그하기 더 쉬운 경우가 많습니다.

다중 스레드 시스템에서 write 접근이 필요 없기 때문에, 리소스 경쟁 조건을 유발하지 않습니다.


반대로 단점도 있습니다.

단일 조각(single piece) 을 추가/삭제/변경 하기 위해, collection 전체를 복사하는 것은 비용이 많이 듭니다.




Question #12

What do you understand about lateinit in Kotlin? When would you consider using it?

(lateinit?)


Answer


생성자에서 변수를 초기화하지 않고, 나중에 초기화하려면 lateinit 키워드를 이용해서 변수를 선언합니다.


초기화되기 전까지는 메모리 할당이 되지 않습니다.

lateinit 은 나중에 초기화가되므로 val 을 이용할 수 없습니다.

primitive type 이나 nullable 은 쓸 수 없습니다.

만약 초기화되기 전에 접근하면 예외가 발생합니다.


lateinit var test: String

fun testFunction() {    
    test = "Interview"    
    println(test.length)        // 9
    test = "Bit"
 }


Android lifecycle 에서 초기화되는 변수

생성자 외부에서 독립적으로 초기화되는 주입된 클래스 변수 (DI)

단위 테스트에서 @Before 주석이 달린 함수에서 테스트 환경 변수




Question #13

Explain lazy initialization in the context of Kotlin.

(lazy initialization?)


Answer


객체 초기화에 시간이 너무 많이 소요되는 경우, lazy initialization 이 유용합니다.

lazy initialization 을 이용하여 객체를 선언하면, 객체가 이용될 때 한번만 객체가 초기화됩니다.

만약 이용하지 않으면, 초기화도 되지 않습니다.


class FastClass {
    private val slowObject: SlowClass by lazy {        
        println("Slow Object initialised")        
        SlowClass()
    }        

   fun access() {
        println(slowObject)
    }
 } 


fun main(args: Array<String>) {
    val fastClass = FastClass()
    println("FastClass initialised")
    fastClass.access()
    fastClass.access()
 }


// FastClass initialised
// Slow Object initialised
// SlowClass@2b12fkk7  
// SlowClass@2b12fkk7




Question #14

Differentiate between lateinit and lazy initialisation. Explain the cases when you should use lateinit and when you should use lazy initialisation.

(lateinit 과 lazy initialization 차이는? 언제 써야하나?)


Answer


lateinit

초기화 시점 지연

프로그램 어디에서나 초기화 가능

여러번 초기화 가능

Thread-safe 하지 않다.


var 만 가능

primitive type 불가능


lazy initialization


나중에 사용할 때만 초기화

프로그램 전체에서 개체의 단일 복사본이 유지 관리됩니다.

초기화 람다만 가능

한번만 초기화 가능

Thread-safe 하다


val 만 가능

primitive type 가능


아래와 같이 나눌 수 있습니다.

속성이 변경 가능한 경우 lateinit 을 이용

속성이 외부에서 설정되는 경우 lateinit 이용

한 번만 초기화되고, 모두에게 공유되어 있고, 더 내부적으로 설정되어 있는 경우 lazy initialization 이용

lateinit 을 이용할 수 있지만, lazy initialization 을 이용하면 캡슐화가 더 잘되는 경우




Question #15

Explain scope functions in the context of Kotlin. What are the different types of Scope functions available in Kotlin?

(scope functions?)


Answer


Kotlin 표준 라이브러리에는 객체 context 내에서 코드 블록 실행을 지원하는 많은 함수가 있다.

람다 식을 이용하여 객체에서 이러한 함수를 호출하면 임시 scope 가 생성되는데, 이를 scope function 이라 부른다.


https://kotlinlang.org/docs/scope-functions.html#function-selection



let 

let 함수는 null safety call 에 자주 쓰입니다.


val str: String? = "Hello"   

val length = str?.let {
    println(it)    
}

// Hello


apply

객체를 초기화하기 위해 자주 쓰입니다.


val adam = Person("Adam").apply { 
    age = 20                       // same as this.age = 20 or adam.age = 20
    city = "London"
}


with

람다 결과를 제공하지 않고, context 객체에 대한 함수를 호출할 때 권장됩니다.

val numbers = mutableListOf("one", "two", "three")

with(numbers) {
    println("'with' is called with argument $this")   
    println("It contains $size elements")
}


run

'let'과 'with' 함수의 조합입니다. 

객체 람다가 반환 값의 초기화와 계산을 모두 포함하는 경우 이것이 사용하는 방법입니다. 

run 을 이용하여 null-safe 하게 계산을 수행할 수 있습니다.


val service = MultiportService("https://example.kotlinlang.org", 80)

val result = service.run {
    port = 8080
    query(prepareRequest() + " to port $port")
}

// the same code written with let() function:
val letResult = service.let {
    it.port = 8080
    it.query(it.prepareRequest() + " to port ${it.port}")
}


also

객체 멤버가 초기화된 후 추가 작업을 수행해야 할 때 이용됩니다.


fun getRandomInt(): Int {
    return Random.nextInt(100).also {
        writeToLog("getRandomInt() generated value $it")
    }
}




Question #16

What do you understand about sealed classes in Kotlin?

(sealed class?)


Answer


sealed 클래스는 sub 클래스의 집합을 가진 클래스입니다.

sub 클래스의 종류를 제한하고 있습니다.

이를 통해 런타임이 아닌 컴파일 타임에 타입의 유효성을 검사하기 때문에 타입 안정성이 보장됩니다.


sealed class Sample {
     class A : Sample() {
         fun print() {             
            println("This is the subclass A of sealed class Sample")
         }
     }     

    class B : Sample() {
         fun print() {
             println("This is the subclass B of sealed class Sample")
         }
     }
}

fun main() {
     val obj1 = Sample.B()
     obj1.print()
     
    val obj2 = Sample.A()
     obj2.print()
}




Question #17

What do you understand about the backing field in Kotlin?

(back field?)


Answer


back field 는 접근자(getter/setter) 내부에서만 이용할 수 있는 자동 생성된 field 입니다.


custom getter/setter 를 이용할 때, 필요한 경우 field 식별자로 접근할 수 있습니다.

var marks: Int = someValue        
    get() = field        
    set(value) {
            field = value
     }




Question #18

What are some of the disadvantages of Kotlin?

(Kotlin 단점은?)


Answer


internal, crossinline, expect, reified, sealed, inner, open 같이 Java 에는 없는 명확하지 않은 의미를 가진 키워드들이 있다.

Checked exception  없기 때문에, 이로인한 안정성을 보장받지 못한다고 느낄 수 있습니다.

data class 를 정의하면 많은 코드들이 자동으로 추가되기 때문에, 개발자 입장에선 디버깅 하기에 숨겨져 있다고 느낄 수 있습니다.

Java 에 비해 자료나 학습 자원 부족합니다.

Kotlin 에는 다양한 컴파일 속도가 있습니다.
일부 상황에서는 Java 가 훨씬 빠릅니다.

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari