코틀린 완벽가이드 4장 연습문제

searchortype·2023년 6월 4일
1
post-thumbnail

예제 코드

https://github.com/leechGamer/kotlin/commits/main/4

    1. 코틀린 클래스의 기본적인 구조를 설명하라. 자바 클래스와 비교하면 어떤 차이가 있는가?
    • 코틀린 클래스의 기본적인 구조

      class test {
      	private val a: Int
      	init{
      		println("test!")
      	}
      	private val b: Int
      	fun isTest (): Boolean = false
      }
    • 자바 클래스와의 차이
      - 코틀린은 클라이언트의 코드를 바꾸지 않아도 프로퍼티의 구현을 바꿀 수 있다.
      - 코틀린은 class 안에 getter/setter를 제공함으로써 자바처럼 get으로 시작하는 함수를 새로 만들지 않아도 된다.

      class Person(private var _name: String) {
          val name: String
              get() = _name.toUpperCase() // 커스텀 게터를 추가하여 소문자로 입력된 이름을 대문자로 반환
      
          var age: Int = 0
              set(value) {
                  field = if (value >= 0) value else 0 // 커스텀 세터를 추가하여 음수로 설정되는 나이를 0으로 제한
              }
      }

    1. 주생성자란 무엇인가?

      • 주생성자: 필드초기화와 init을 클래스 본문에 나타난 순서대로 적은 것. 필드초기화가 먼저 일어나고 init이 실행되는게 아니다. 블록에 적어놓은 순서대로 실행되는게 맞다.
      class MyClass {
          private val firstField: String = "First Field"
          private val secondField: Int
      
          init {
              secondField = 42
          }
      
          private val thirdField: Boolean = true
      }

    1. 부생성자란 무엇인가? 클래스에 어떤 생성자(또는 생성자들)를 포함시킬지와 주생성자 외에 부생성자가 더 필요할지를 어떻게 결정하는가?
      - 코틀린의 부 생성자(Secondary Constructor)는 클래스에 추가적인 생성 방법을 제공하는 생성자를 의미한다. 이는 클래스가 다양한 매개변수를 통해 생성될 수 있도록 하는데 유용합니다. 부 생성자는 constructor 키워드를 사용해 선언.

      • 주생성자와 다른 타입을 받고 싶을때 부생성자를 쓴다.
      class Person(val name: String) {
      var age: Int = 0
      
      constructor(name: String, age: Int) : this(name) {
          this.age = age
      	}
      }

    1. 코틀린이 지원하는 멤버 가시성은 무엇인가? 자바의 가시성과 어떤 차이가 있는가?
    • private
      • 거의 동일하나 일부 조금 다름
    • protected
      • java
        • 같은 패키지라면 패키지 내의모든 클래스에서 접근 가능
        • 다른 패키지라면 상속관계의 클래스만 접근가능
      • kotlin
        • 다른 패키지라면 상속관계의 클래스만 접근가능
    • internal
      • java
        • 없음. 대신 코틀린에 없는 default (package-private)가 있음
        • java9 직소, manifest.mf , list.pls모듈로 하면 흉내낼 수 있다.
      • kotlin
        • 모듈* 내부에서만 쓸 수 있다.
          • 모듈*
            • 함께 컴파일되는 파일 전부를 뜻함
            • 동일한 프로젝트 내의 모듈: 동일한 컴파일 단위: 동일한 패키지:
    • public
      • 둘다 동일하다

    1. 내포된 클래스(nested class) 중에서 내부 클래스(inner class)와 비내부 클래스의 차이는 무엇인가? 각각에 해당하는 자바 클래스와는 어떤 차이가 있는가 비교하라.
      • 언제 inner class를 써야할까
        • 합성으로 객체구조를 작성하되 합성한 class에 가시성을 지정할 때
      • 비내부 클래스와의 차이
        class Person (private val  a: Integer) {
        	inner class possesion() {
        		fun getOwner(): String = this@Person.a // 접근 됨
        	}
        }
        
        class Possesion(val person:Person) {
        	person.a // 접근 안됨
        }

    1. 함수 본문에서 클래스를 정의할 수 있는가? 정의할 수 있다면 이렇게 정의한 클래스에는 어떤제약이 있을까?
    • 지역클래스를 통해 함수 안에 클래스를 정의할 수 있다.
    • 예제
  fun someFunction() {
    class LocalClass {
        fun sayHello() {
            println("Hello from the local class!")
        }
    }

    val localInstance = LocalClass()
    localInstance.sayHello()
}
someFunction() // 이 함수를 호출하면 "Hello from the local class!"를 출력합니다.
  • 지역클래스를 통해 정의한 클래스의 제약
      1. 범위 제한: 지역 클래스는 특정 블록(함수, 메서드, 초기화 블록 등) 내에서만 정의될 수 있다. 따라서 해당 블록을 벗어나면 클래스에 대한 접근이 불가능
      1. 외부 변수 사용: 지역 클래스는 외부 범위에 있는 변수를 참조할 수 있다. 하지만 참조하는 외부 변수는 final 또는 effectively final로 선언되어야 한다. 이는 변수가 클래스 내에서 변하지 않거나, 변경되더라도 람다 식과 마찬가지로 동일한 값으로 유지되어야 함을 의미한다.
      1. 정적 멤버의 사용 불가: 지역 클래스 내에서는 정적(static) 멤버를 참조할 수 없다. 정적 멤버는 해당 클래스의 인스턴스와 독립적으로 존재하며, 지역 클래스는 특정 인스턴스 내에서만 유효하기 때문이다.
      1. 접근 제한자 제약: 지역 클래스는 private 또는 final로만 선언될 수 있다. private 접근 제한자를 사용하면 해당 클래스는 외부로부터 완전히 은닉되고, final 키워드를 사용하면 상속을 방지하여 클래스의 확장을 막을 수 있다.

    1. 지연 초기화 메커니즘의 요지는 무엇인가? 널이 될 수 있는 프로퍼티 대신 lateinit 프로퍼티를 사용할 경우 어떤 장점이 있는가?
    • 매커니즘의 요지
      • 필요한 시점에 객체를 초기화 한다.
    • lateinit의 장점
      • nullable한 타입의 객체의 사용할 때 매번 null체크를 해야한다. 하지만 lateinit을 사용할 경우 최초 사용시 초기화를 하기때문에 null체크를 할 필요가 없다.

    1. 커스텀 프로퍼티 접근자란 무엇인가? 코틀린 접근자와 자바의 게터/세터를 비교하라
class Example {
    var counter = 0
        set(value) {
            if (value >= 0) {
                field = value
            }
        }
}
  • 커스텀 프로퍼티 접근자
    - 프로퍼티 값을 읽거나 쓸 때 호출되는 함수
    • 코틀린 접근자와 자바의 getter/setter 비교
      • 코틀린은 getset 키워드를 사용하여 접근자를 정의하고, 자바는 getset으로 시작하는 메서드를 작성합니다.

    1. 클래스를 사용하는 클라이언트 입장에서 볼 때 실질적으로 val 과 같은 역할을 하는 읽기 전용 프로퍼티를 val 을 쓰지 않고 만들 수 있는가? 반대로 쓸 수만 있는 프로퍼티는 어떻게 만들 수 있을까?
    • 읽기 전용 프로퍼티
      커스텀 getter을 정의해서 사용하면 된다. 클래스 내부에서만 값을 변경할 수 있게 private으로 선언한다.

      import java.util.*
      
      class Person(name: String) {
          private var _name: String = name
          val name: String
              get() = _name
      }

    1. lazy 프로퍼티를 사용해 지연 계산을 달성하는 방법은 무엇인가? lazy와 lateinit 프로퍼티를 비교해보라

      이 둘의 가장 큰 차이는 외부에서 값을 주입하는지 (lateinit) 내부에서 주입하는지 (lazy)에 따라서 나눌 수 있다.

    • lateinit은 언제 쓰는게 좋을까?
      • 객체 주입이 확실한 경우에 (ex. 제어의 역전 같은 framework이 알아서 해줄거야 같은)

    1. 객체 선언이란 무엇인가? 코틀린 객체와 자바에서 일반적인 싱글턴 구현 패턴을 비교하라.
      코틀린에서 object 키워드를 사용하여 객체 선언을 할 수 있다. 객체 선언은 전역 싱글턴 인스턴스를 생성하는 데 사용된다.
object Singleton {
    fun doSomething() {
        println("Singleton doing something")
    }
}

위 코드에서 Singleton 객체는 전역 싱글턴 인스턴스이다. 이를 사용하려면 다음과 같이 호출하면 된다.

Singleton.doSomething()

객체 선언과 자바의 전통적인 싱글턴 구현 패턴을 비교하면 다음과 같다:

  1. 문법: 코틀린에서는 object 키워드를 사용하여 객체 선언을 직접적으로 생성할 수 있다. 반면에 자바에서는 전통적인 싱글턴 구현 패턴을 사용해야 한다.
  2. 인스턴스 생성: 코틀린에서 객체 선언은 전역 싱글턴 인스턴스를 생성하므로 인스턴스 생성에 대한 고민이 없다. 반면에 자바에서는 싱글턴 객체를 생성하기 위해 복잡한 코드와 디자인 패턴을 사용해야 한다.
  3. 스레드 안전성: 코틀린에서는 객체 선언이 스레드 안전성을 보장합니다. 반면에 자바에서는 스레드 안전성을 보장하기 위해 추가적인 코드와 동기화 메커니즘을 사용해야 한다.
  • 추가로

이와 대조적으로, 자바에서는 싱글턴 패턴을 구현하기 위해 더 많은 코드를 작성해야 한다. 자바에서는 보통 private 생성자와 static 메소드를 사용하여 싱글턴 패턴을 구현한다.

public class Singleton {
    private static Singleton instance;
    public static String info = "I'm a singleton object";

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

이 경우에는 Singleton.getInstance()를 통해 싱글턴 인스턴스에 접근해야 한다.

따라서 코틀린의 객체 선언은 자바의 싱글턴 패턴 구현보다 훨씬 간결하며, 코드의 가독성과 효율성을 높여준다.


    1. 클래스와 비교할 때 객체 선언은 어떤 제약이 있는가?
      1. 단일 인스턴스: 객체 선언은 단일 인스턴스만을 생성한다. 즉, 객체 선언은 클래스와 달리 다수의 인스턴스를 생성할 수 없다.
      1. 이름: 객체 선언은 이름을 가지고 있다. 이 이름은 해당 객체에 대한 참조를 통해 객체의 멤버에 접근하는 데 사용된다.
      1. 상속: 객체 선언은 상속될 수 없다. 즉, 다른 클래스나 인터페이스를 상속할 수 없다. 하지만 인터페이스를 구현할 수는 있다.
      1. 생성자: 객체 선언은 생성자를 가질 수 없다. 객체 선언은 자동으로 인스턴스화되며, 생성자 호출 없이 초기화된다.

    1. 일반 객체(object)와 동반 객체(companion object)의 차이는 무엇인가?
    • companion object는 외부클래스에서 private로 선언한 property에 접근할 수 있지만
    • object는 접근이 불가
      class test {
      	private a :Int
      
      	companion object {
      		fun check() {
      			test.a
      		}
      	}
      }
      
      object test2 {
      	fun check() {
      			test.a // error
      		}
      }

    1. 코틀린 동반 객체 (companion object)와 자바의 static 내포 객체를 비교하라.

      interface A
      class Test(a:Int) {
      	companion object:Runnable {
      		override fun run() {}
      	}
      }
      
      class TestA(a:Int) {
      	companion object:Runnable {
      		override fun run() {}
      	}
      }
      
      object Test2 {
      	fun check(runnable: Runnable) {}
      }
      
      fun main() {
      	Test2.check(Test) // 이렇게만 넘겨도 됨 암묵적으로 Test.Companion의 약자
      	Test2.check(TestA) // 그래서 클래스 간 상속관계가 없음에도 class들의 companion에만 동시성을 부여할 수 있다.
      }

      위 코드에서 본 것처럼 companion static 수준의 인터페이스를 만들 수 있다.

      자바에서의 static 내포 객체의 경우 공통점을 만들어 낼 수 없다.


    1. 자바의 익명 클래스에 해당하는 코틀린 기능은 무엇인가? 이런 코틀린 언어의 기능을 어떻게 사용할수있을까?
    • 익명클래스는 어떠한 형이라고 보면 된다.

    • 함수 scope 안에서만 유효하기 때문에 한번만 쓸거면 익명클래스로 사용해도 충분하다 하지만 재활용되어야 한다면 class를 사용하는 것이 낫다.

      fun main() {
          val o = object { // 익명 객체 타입이 추론된다.
              val x = readLine()!!.toInt()
              val y = readLine()!!.toInt()
          }
          println(o.x + o.y)
      }

0개의 댓글