룬아님의 취중코딩

코틀린 인터뷰 질문 (번역) 본문

개발/Kotlin

코틀린 인터뷰 질문 (번역)

룬아님 2022. 5. 28. 17:27

원본 : https://androidchef.com/kotlin-interview-questions-15755f899199

 

Kotlin Interview Questions — Beginner to Pro

“Kotlin is a programming language that makes developers happier :),” Quoted by JetBrains and the open-source contributors. I really have…

androidchef.com

해당 내용을 한국어로 번역만 하였습니다.

 

Kotlin에서 val과 var의 차이점은 무엇입니까?

Kotlin에서 val은 Java의 final 키워드와 동일합니다. 값이 지정되면 변경할 수 없으며 변경할 수 없습니다. var는 일반 변수처럼 변경 가능합니다.

var a = 10 
a = 12 // compiles
val b = 20
b = 21 // compile time error: val cannot be reassigned

 

 

아래 문장이 문법적으로 맞을까요?

var number // line 1
number = 10 //line 2

 

아니요. 컴파일 타임 오류가 발생합니다. Kotlin은 컴파일 타임에 타입 유추를 사용합니다. 이 유추는 할당된 값이나 주어진 데이터 유형의 주석에서 변수 타입을 유추합니다. 따라서 위의 코드 라인은 변수 "number"가 명시적으로 언급하지 않고 컴파일러가 어떤 타입의 숫자인지 확인할 수 없기 때문에 오류가 발생합니다.

 

 

다음 프로그램의 결과값을 알려주세요

fun main() {
   var employee1 = Employee("maria", 1)
   var employee2 = Employee("john", 2)
   var employee3 = Employee("peter", 3)
   var employee4 = Employee("peter", 3)
   println(employee1 == employee2) 
   println(employee3 == employee4)
   println(employee1.equals(employee2))
   println(employee3.equals(employee4))  
 }
class Employee(var name: String, var id: Int) {
   override fun equals(obj: Any?): Boolean {
       if (obj is Employee) {
           return name == obj.name && id == obj.id
       }
       return false
   }
}

false, true, false, true

== 및 .equals는 모두 Kotlin에서 동일합니다. Java와 달리 둘 다 구조적 동등성을 확인합니다.

 

 

Kotlin에서 주소값이 동일한 것을 어떻게 확인할 수 있을까요?

=== 이 삼중 등호 연산자를 사용합니다.

 

 

Kotlin에서 Unit과 Nothing의 차이점은 무엇인가요?

Unit

"Java의 void와 동일합니다."

함수가 유용한 것을 반환하지 않거나 리턴해줄 값이 아무것도 없으면 암시적으로 Unit을 반환한다고 말합니다. 그리고 이러한 기능은 부작용이 있는 작업을 수행할 수 있습니다. 무언가를 기록/인쇄하거나 반환 값 없이 조작을 수행합니다.

fun printHelloUnit(name: String?): Unit {
    if (name != null)
        println("Hello $name")
    else
        println("Hi there!")
}

//The Unit return type declaration is also optional. The above code is equivalent to:

fun printHello(name: String?) {
    if (name != null)
        println("Hello $name")
    else
        println("Hi there!")
}

위의 예에서 반환 유형을 Unit으로 명시적으로 언급하면 컴파일러는 Unit 유형이 중복되므로 명시적 언급을 제거하도록 제안합니다.

Nothing

문자 그대로 아무 것도 "생명으로의 복귀가 없으며 게임은 거기서 끝납니다 ;)"를 의미합니다. 즉, 함수가 여기에서 반환되지 않고 예외가 발생하거나 무한 루프에 들어갈 것입니다.

또한 반환 유형이 Nothing인 함수를 호출한 후에 작성하는 모든 코드는 컴파일러에서 연결할 수 없는 것으로 표시됩니다.

결론적으로 :

Unit"내가 돌아올 것이지만 당신이 관심을 가질만한 가치는 없습니다" 라고 말합니다. 그리고 Nothing"나는 결코 돌아 오지 않을 것입니다" 라고 말하는 것과 같습니다. 따라서 기능의 특성을 보다 명확하게 개별적으로 언급하는 데 도움이 됩니다.

class NothingClass {

    fun returnName(isSuccess: Boolean): String? {
        return if (isSuccess) {
            println("Sara")
            "Sara"
        } else null
    }

    fun reportError(): Nothing {
        println("no name found")
        throw RuntimeException()
        // var i = 1 // unreachable code
    }
// here if you don't specify Nothing explicitly, it shows compile time error
fun iWillAlwaysThrowException() : Nothing =  throwException("Unnecessary Exception")
}

fun main() {
val nothingClass = NothingClass()
val name: String = nothingClass.returnName(true) ?: nothingClass.reportError() // Compiles and the return type is String or Nothing 
val noName: String = nothingClass.returnName(false) ?: nothingClass.reportException()
}

 

 

다음 코드의 출력은 무엇입니까

var student = Student("Elsa", 1234)  
var twinStudent = student

fun main() {
    twinStudent.name = "Rachel"
    println(student.name)
}

Rachel입니다.

여기서 student와 twinStudent는 동일한 객체를 가리키며, 이는 메모리에 동일한 참조가 있음을 의미합니다. 둘 중 하나에 내부적으로 도입된 모든 변경 사항은 다른 하나에 반영됩니다. 이는 전체 개체가 아니라 개체의 속성에 대한 모든 변경을 의미합니다.

 

 

Kotlin에서 싱글톤 클래스를 어떻게 생성합니까?

"object" 라는 키워드를 사용하십시오. 그게 전부입니다!

object classname {
}

이제 이 코드가 의미하는 바에 대한 요지를 빠르게 알려 드리겠습니다.

가볍게 생각하지 마세요. Singleton 클래스/패턴은 사용 언어에 관계없이 매우 중요한 소프트웨어 디자인 패턴이기 때문입니다.

우선 그 의미를 이해해야 합니다.

싱글톤 클래스는 말 그대로 "Single all the time" 을 의미합니다. 여러 번 인스턴스화되는 것으로 제한되며 싱글톤 클래스의 인스턴스가 생성되면 애플리케이션/앱 전체에서 이 클래스의 유일한 인스턴스로 영원히 유지됩니다.

Java에서 우리는 싱글톤 클래스를 생성하기 위해 몇 가지 단계를 따릅니다 :

  1. private constructor
  2. 다음과 같은 정적 getInstance() 메서드:
    if (클래스의 인스턴스가 존재하지 않는 경우) -> 하나 생성,
    else -> 기존 인스턴스 반환
  3. 스레드 안전성을 보장하기 위해 getInstance() 메서드를 동기화합니다.

그것은 많은 보일러플레이트입니다. Kotlin은 위의 모든 단계를 단일 단계로 좁혀 개발자의 작업을 더 쉽게 만듭니다.

다음 작업만 하면 됩니다 :

object classname {
}

이제 백스테이지에서 무슨 일이 일어나는지 이해하려면 다음 코드를 디컴파일해보세요.

도구 ▸ Kotlin ▸ Kotlin 바이트코드 표시로 이동하고 Kotlin 바이트코드 창 상단에 있는 디컴파일 버튼을 클릭합니다. 그러면 다음을 볼 수 있습니다.

public final class classname {
   @NotNull
   public static final classname INSTANCE;
private classname() {
   }
static {
      classname var0 = new classname();
      INSTANCE = var0;
   }
}

 

 

companion object와 object의 차이점은 무엇입니까?

Kotlin에는 싱글톤에 대한 "object"가 있습니다. 그리고 "companion object"는 object 개념의 변형입니다. 특정 클래스에 속하는 싱글톤 객체입니다. 그것은 클래스의 동반자 같은 것입니다. 그것은 독립적으로 설 수 없습니다.

캥거루 주머니 속의 아기처럼. 특정 클래스의 동반자이기 때문에 외부 클래스 인스턴스를 통해 모든 개인 수준 메서드 및 속성에 액세스할 수 있습니다.

여기 케이크 가게의 예가 있습니다. 새 케이크가 구워질 때마다 cakeCount를 증가시켜야 하고 변수 cakeCount는 모든 케이크 인스턴스에서 공유되어야 합니다.

class Cake(var flavour: String) {

    init {
        println("Baked with Love : $flavour cake ")
        incrementCakeCount()
    }

    private fun incrementCakeCount() {
        cakeCount += 1
    }

    companion object {
        var cakeCount = 0
    }
}

fun main() {
    val cake1 = Cake("Chocolate")
    val cake2 = Cake("Vanilla")
    val cake3 = Cake("Butterscotch")

    println(Cake.cakeCount)
}
아이디어는 Java의 정적 내부 클래스와 거의 비슷합니다. 그러나 클래스는 여러 번 인스턴스화할 수 있는 Kotlin의 클래스이며 "object" 키워드는 단일 인스턴스가 있는 실제 객체에 대한 것이고 모든 멤버는 인스턴스 멤버라는 것을 기억하십시오.

 

 

Kotlin에서 클래스 외부의 함수(A package-level / Top-level function수)를 작성할 수 있습니까?

Java와 달리 Kotlin에서는 클래스 외부에 함수를 작성할 수 있습니다. 함수 논리가 클래스의 속성이나 목적과 무관한 경우 사라져야 합니다.

이것은 Kotlin이 독점적으로 객체 지향 언어가 아니며 기능적으로도 사용될 수 있기 때문에 가능합니다.

그리고 이것들은 클래스가 아니라 Kotlin 파일 내의 함수와 멤버입니다.

이전의 케이크 가게 예제에서 재료를 측정하는 동안 컵을 ml로 변환하는 함수가 필요한 경우 케이크 클래스의 일부일 필요는 없습니다. 따라서 베이킹에 대한 모든 도우미 기능이 포함된 BakingHelpers라는 별도의 파일에 저장하겠습니다.

package com.basic.bakinghelpers

fun convertCupsToML(cup: Int): Int {
    return cup * 237
}

결론적으로 다음과 같습니다.

  • 그것은 일을 간단하고 깨끗하게 만듭니다
  • 함수 호출자는 이 함수를 사용하기 위해 객체를 선언하고 생성할 필요가 없습니다.

 

 

다음 코드는 컴파일이 가능할까요?

class Bird(name: String) {
 fun capitalizeBirdName(){
        name.replaceFirstChar { it.uppercase() }
    }
}

아니요. 컴파일되지 않습니다. 여기서 "name"은 생성자의 property가 아니라 생성자의 parameter일 뿐입니다. 우리는 클래스의 함수 내에서 parameter "name"에 액세스하려고 시도하고 있으며 여기에는 제한이 있습니다.

그렇다면 생성자 property와 생성자 parameter의 차이점은 무엇입니까?

parameter는 일반적으로 함수 서명의 일부인 변수일 뿐입니다. 그리고 생성자도 함수의 한 종류이고 parameter를 가질 수 있으며 매개변수화된 생성자라고 합니다.

여기서 parameter 이름은 생성자의 init{} 블록 내에서만 또는 클래스의 다른 속성 선언과 함께 액세스할 수 있습니다.

코드를 수정하기 위해 init 블록에서 조작을 수행하거나 parameter 이름과 동일한 property을 만들 수 있습니다.

class Bird(name: String) {
// usage in the init block  
 init {
        println(name.replaceFirstChar { it.uppercase() })
    }
}

or

class Bird(name: String) {
// Here we create a property name and assign parameter to the property name
var name : String =  name
fun capitalizeBirdName(){
    name.replaceFirstChar { it.uppercase() }
}
}

그리고 위의 코드는 parameter를 생성자 자체 내에서 property로 변환하여 더 최적화할 수 있습니다. parameter에 val 또는 var를 추가하기만 하면 생성자 parameter를 property로 변환할 수 있습니다.

class Bird(var name: String) {
fun capitalizeBirdName(){
        name.replaceFirstChar { it.uppercase() }
    }
}

property은 일반 parameter 그 이상입니다.

  • 기본 setter와 getter를 가질 수 있습니다.
  • 클래스 내 어디에서나 액세스할 수 있습니다.
  • 데이터를 저장할 필드가 있을 수 있습니다.

위의 코드에서 "name"은 더 이상 parameter가 아니라 클래스의 property이며 클래스의 어느 곳에서나 액세스할 수 있습니다. 이제 코드는 올바르게 컴파일됩니다.

 

Kotlin에 기본 데이터 유형이 있습니까?

글쎄요, 하지만 내부적으로는 개발자가 코드에서 직접 사용하기 위한 것이 아닙니다.

ref:  https://www.youtube.com/watch?v=Ta5wBJsC39s

첫 번째 줄 var i0 = 5를 살펴보겠습니다.

Koltin에서 참조 유형 "Int"를 사용하고 내부적으로 컴파일러는 참조 유형을 기본 유형 "int"로 매핑합니다. 변수 i1에 대한 두 번째 줄에도 동일하게 적용됩니다.

변수 i0과 i1 모두에 대해 다음과 같이 됩니다.

  • 스택에 푸시됩니다.
  • 다른 기본 유형과 마찬가지로 값 5를 직접 저장합니다.

그러나 세 번째 줄의 경우 변수 i2는 nullable이므로 컴파일러는 참조 유형을 유지하고 기본 래퍼를 제거하지 않습니다. 값 4를 "Integer" 유형으로 자동 상자에 넣고 힙 메모리에 개체로 유지합니다.

그리고 변수 i2의 경우:

  • 스택으로 푸시됩니다.
  • 값 4를 정수 유형으로 autobox에 넣고 힙 메모리에 개체를 만듭니다.
  • 값 4를 i2에 직접 저장하는 대신 객체 i2의 참조(주소)를 힙 메모리에 저장합니다.

결론적으로 Kotlin은 개발자를 귀찮게 하지 않고 가능한 한 내부에서 참조 유형을 기본 유형으로 매핑하는 작업을 수행합니다. 개발자의 관점에서 우리는 Kotlin에서 기본 유형을 처리하지 않고 참조 유형만 만지게 됩니다.

나는 개인적으로 이것이 매우 편리한 기능이라고 생각하며 많은 혼란에서 우리를 확실히 구해줍니다.

 

다음 코드는 컴파일될까요? (여기서 우리는 Int 유형의 변수를 Long에 할당하려고 합니다)

val a: Int = 1 // A boxed Int (java.lang.Integer)
val b: Long = a 

Java에서는 암시적 유형 캐스팅 또는 기본 유형에 대한 type 승격(또는 valueOf() 메소드를 통해)을 통해 더 작은 유형을 더 큰 유형에 할당하는 것이 가능하다는 것을 알고 있습니다.

그러나 Kotlin에서는 불가능하며 이는 2행에 컴파일 타임 오류가 발생합니다. "Kotlin에서 더 작은 유형은 암시적으로 더 큰 유형으로 변환되지 않습니다."

Int는 32비트 정수이고 long은 64비트 정수이지만 더 작은 유형을 더 큰 유형에 할당하면 이러한 데이터 유형의 평등과 ID가 손상될 수 있으므로 제한됩니다. 따라서 이 작업을 수행하려면 Int를 Long으로 명시적으로 변환해야 합니다.

val a: Int= = 1 // A boxed Int (java.lang.Integer)
val b: Long = a.toLong()

이제 해당 코드가 올바르게 컴파일됩니다.

반응형
Comments