[properties] Kotlin- “지연에 의한”대 “lateinit”를 사용한 속성 초기화

Kotlin에서는 생성자 내부 또는 클래스 본문 상단에서 클래스 속성을 초기화하지 않으려는 경우 기본적으로 언어 참조의 두 가지 옵션이 있습니다.

  1. 게으른 초기화

lazy ()는 람다를 가져오고 lazy 속성을 구현하기위한 대리자로 사용할 수있는 Lazy 인스턴스를 반환하는 함수입니다. get ()에 대한 첫 번째 호출은 lazy ()에 전달 된 람다를 실행하고 결과, 후속 호출을 기억합니다 get ()은 단순히 기억 된 결과를 반환합니다.

public class Hello {

   val myLazyString: String by lazy { "Hello" }

}

이에, 어디든지 첫 번째 통화와 subquential 전화, 그래서 myLazyString 돌아갑니다 “안녕하세요”

  1. 늦은 초기화

일반적으로 null이 아닌 유형으로 선언 된 속성은 생성자에서 초기화해야합니다. 그러나 이것은 종종 편리하지 않습니다. 예를 들어, 속성은 의존성 주입을 통해 또는 단위 테스트의 설정 방법으로 초기화 될 수 있습니다. 이 경우 생성자에 null이 아닌 이니셜 라이저를 제공 할 수 없지만 클래스 본문 내에서 속성을 참조 할 때 null 검사를 피하려고합니다.

이 경우를 처리하기 위해 lateinit 수정자를 사용하여 특성을 표시 할 수 있습니다.

public class MyTest {

   lateinit var subject: TestSubject

   @SetUp fun setup() { subject = TestSubject() }

   @Test fun test() { subject.method() }
}

수정자는 클래스 본문 내에 선언 된 var 속성 (기본 생성자는 아님) 및 속성에 사용자 지정 getter 또는 setter가없는 경우에만 사용할 수 있습니다. 특성의 유형은 널이 아니어야하며 기본 유형이 아니어야합니다.

따라서 두 옵션 모두에서 동일한 문제를 해결할 수 있기 때문에이 두 옵션 중에서 올바르게 선택하는 방법은 무엇입니까?



답변

위임 된 속성 lateinit varby lazy { ... }위임 된 속성 의 중요한 차이점은 다음과 같습니다 .

  • lazy { ... }delegate는 val속성 lateinit에만 사용할 수있는 반면 var, final필드에 컴파일 할 수 없으므로 불변성을 보장 할 수 없으므로 s 에만 적용 할 수 있습니다.

  • lateinit var는 값을 저장하는 백킹 필드를 가지며, by lazy { ... }일단 계산 된 값이 저장되는 델리게이트 객체를 만들고 클래스 객체에 델리게이트 인스턴스에 대한 참조를 저장하고 델리게이트 인스턴스와 작동하는 속성에 대한 게터를 생성합니다. 따라서 클래스에 지원 필드가 필요하면 lateinit;

  • vals 외에도 lateinit널 입력 불가능 특성 및 Java 기본 유형에 사용할 수 없습니다 ( null초기화되지 않은 값 에 사용 되기 때문 ).

  • lateinit var예를 들어 프레임 워크 코드 내부에서 객체가 보이는 곳에서 초기화 할 수 있으며 단일 클래스의 다른 객체에 대해 여러 초기화 시나리오가 가능합니다. by lazy { ... }또한 속성에 대한 유일한 이니셜 라이저를 정의하며 서브 클래스에서 속성을 재정의하는 경우에만 변경할 수 있습니다. 미리 알 수없는 방식으로 외부에서 속성을 초기화하려면을 사용하십시오 lateinit.

  • 초기화 by lazy { ... }는 기본적으로 스레드로부터 안전하며 초기화 프로그램이 최대 한 번만 호출되도록 보장합니다 (그러나 다른 lazy과부하 를 사용하여 변경할 수 있음 ). 의 경우 lateinit var멀티 스레드 환경에서 속성을 올바르게 초기화하는 것은 사용자 코드에 달려 있습니다.

  • Lazy인스턴스가 저장 건네 심지어 여러 속성에 사용할 수 있습니다. 반대로 lateinit vars는 추가 런타임 상태를 저장하지 않습니다 ( null초기화되지 않은 값의 필드 에만 해당 ).

  • 당신의 인스턴스에 대한 참조를 보유하고 있다면 Lazy, isInitialized()당신이 이미 초기화되었는지 여부를 확인하기 위해 (그리고 당신이 할 수 있습니다 반사와 같은 예를 얻을 위임로부터 참조). lateinit 속성이 초기화되었는지 확인하려면 Kotlin 1.2부터 사용할property::isInitialized있습니다 .

  • 전달 된 람다 by lazy { ... }클로저에 사용되는 컨텍스트에서 참조를 캡처 할 수 있습니다 . 그러면 참조가 저장되고 속성이 초기화 된 후에 만 ​​참조가 해제됩니다. 이로 인해 Android 활동과 같은 객체 계층 구조가 너무 오랫동안 릴리스되지 않거나 속성에 액세스 할 수 있고 액세스 할 수없는 경우 이니셜 라이저 람다 내부에서 사용하는 것에주의해야합니다.

또한 질문에 언급되지 않은 또 다른 방법 Delegates.notNull()이 있습니다.이 방법은 Java 기본 유형의 속성을 포함하여 null이 아닌 속성의 지연된 초기화에 적합합니다.


답변

또한 hotkey좋은 대답 을 위해 실제로 두 가지 중에서 선택하는 방법은 다음과 같습니다.

lateinit 외부 초기화 용 : 메소드를 호출하여 값을 초기화하기 위해 외부 물건이 필요할 때.

예를 들어

private lateinit var value: MyClass

fun init(externalProperties: Any) {
   value = somethingThatDependsOn(externalProperties)
}

lazy객체 내부의 종속성 만 사용 하는 동안 입니다.


답변

매우 짧고 간결한 답변

lateinit : 최근에 null이 아닌 속성을 초기화합니다

지연 초기화와 달리 lateinit를 사용하면 컴파일러가 null이 아닌 속성 값이 생성자 단계에 저장되어 정상적으로 컴파일되지 않는다는 것을 인식 할 수 있습니다.

게으른 초기화

게으른 by 코 틀린 초기화 지연을 수행 하는 읽기 전용 (val) 속성을 구현할 때 매우 유용 할 수 있습니다 .

by lazy {…}는 선언 된 속성이 아니라 정의 된 속성이 처음 사용되는 초기화 프로그램을 수행합니다.


답변

lateinit 대 게으른

  1. 늦게

    i) 가변 변수 [var]와 함께 사용

    lateinit var name: String       //Allowed
    lateinit val name: String       //Not Allowed

    ii) 널 입력 불가능 데이터 유형 만 허용

    lateinit var name: String       //Allowed
    lateinit var name: String?      //Not Allowed

    iii) 나중에 값을 초기화 할 것을 컴파일러에 약속합니다.

참고 : 초기화하지 않고 lateinit 변수 에 액세스하려고하면 UnInitializedPropertyAccessException이 발생합니다.

  1. 게으른

    i) 지연 초기화는 불필요한 객체 초기화를 방지하도록 설계되었습니다.

    ii) 변수를 사용하지 않으면 변수가 초기화되지 않습니다.

    iii) 한 번만 초기화됩니다. 다음에 사용할 때 캐시 메모리에서 값을 얻습니다.

    iv) 스레드 안전 (처음 사용 된 스레드에서 초기화됩니다. 다른 스레드는 캐시에 저장된 동일한 값을 사용합니다).

    v) 변수는 val 만 가능 .

    vi) 변수는 널 입력이 불가능 합니다.


답변

모든 위대한 답변 외에도 게으른 로딩이라는 개념이 있습니다.

지연 로딩은 필요한 시점까지 객체의 초기화를 연기하기 위해 컴퓨터 프로그래밍에서 일반적으로 사용되는 디자인 패턴입니다.

올바르게 사용하면 응용 프로그램의 로딩 시간을 줄일 수 있습니다. 그리고 Kotlin의 구현 방식은 lazy()필요할 때마다 필요한 값을 변수에로드하는 것입니다.

그러나 lateinit는 변수가 null이거나 비어 있지 않다는 것을 확신하고 사용하기 전에 초기화됩니다 (예 : android의 onResume()메소드에서). 그래서 nullable 유형으로 선언하고 싶지 않습니다.


답변

위의 모든 것이 정확하지만 사실 간단한 설명 중 하나 LAZY —- 처음 사용할 때까지 객체의 인스턴스 생성을 지연하려는 경우가 있습니다. 이 기술을 지연 초기화 또는 지연 인스턴스화라고합니다. 지연 초기화의 주요 목적은 성능을 높이고 메모리 공간을 줄이는 것입니다. 유형의 인스턴스를 인스턴스화하는 데 계산 비용이 많이 들고 프로그램에서 실제로 사용하지 않는 경우 CPU주기를 지연 시키거나 낭비하지 않을 수 있습니다.


답변

Spring 컨테이너를 사용하고 널 입력 불가능 Bean 필드를 초기화하려는 lateinit경우 더 적합합니다.

    @Autowired
    lateinit var myBean: MyBean