나는 Scala가 제공하는 것을 알아 차렸다 lazy vals
. 그러나 나는 그들이 무엇을 얻지 못합니다.
scala> val x = 15
x: Int = 15
scala> lazy val y = 13
y: Int = <lazy>
scala> x
res0: Int = 15
scala> y
res1: Int = 13
REPL의 쇼가 y
A는 lazy val
하지만, 어떻게 정상적인 다르다 val
?
답변
이들의 차이점은 a val
는 정의 lazy val
될 때 실행되는 반면 a 는 처음 액세스 할 때 실행된다는 것입니다.
scala> val x = { println("x"); 15 }
x
x: Int = 15
scala> lazy val y = { println("y"); 13 }
y: Int = <lazy>
scala> x
res2: Int = 15
scala> y
y
res3: Int = 13
scala> y
res4: Int = 13
(으로 정의 된 def
) 메소드와 달리 a lazy val
는 한 번 실행 된 후 다시는 실행되지 않습니다. 작업을 완료하는 데 시간이 오래 걸리고 나중에 사용되는지 확실하지 않은 경우에 유용합니다.
scala> class X { val x = { Thread.sleep(2000); 15 } }
defined class X
scala> class Y { lazy val y = { Thread.sleep(2000); 13 } }
defined class Y
scala> new X
res5: X = X@262505b7 // we have to wait two seconds to the result
scala> new Y
res6: Y = Y@1555bd22 // this appears immediately
여기서 값 x
을 y
사용하지 않으면 x
자원을 불필요하게 낭비합니다. 우리가 가정하는 경우 y
부작용이없고, 우리가 액세스 빈도를 모르는 (결코 한 번, 수천 번)는로 선언하는 쓸모없는 def
우리가 여러 번 실행하지 않으입니다.
lazy vals
구현 방법을 알고 싶다면 이 질문을 참조하십시오 .
답변
이 기능은 값 비싼 계산을 지연시킬뿐만 아니라 상호 의존적이거나 주기적 구조를 구성하는 데 유용합니다. 예를 들어 스택 오버플로가 발생합니다.
trait Foo { val foo: Foo }
case class Fee extends Foo { val foo = Faa() }
case class Faa extends Foo { val foo = Fee() }
println(Fee().foo)
//StackOverflowException
그러나 게으른 발에는 잘 작동합니다.
trait Foo { val foo: Foo }
case class Fee extends Foo { lazy val foo = Faa() }
case class Faa extends Foo { lazy val foo = Fee() }
println(Fee().foo)
//Faa()
답변
나는 대답이 주어진다는 것을 이해하지만 나 같은 초보자도 쉽게 이해할 수 있도록 간단한 예를 썼습니다.
var x = { println("x"); 15 }
lazy val y = { println("y"); x+1 }
println("-----")
x = 17
println("y is: " + y)
위 코드의 출력은 다음과 같습니다.
x
-----
y
y is: 18
보시다시피, x는 초기화 될 때 인쇄되지만 동일한 방식으로 초기화 될 때 y는 인쇄되지 않습니다 (y를 초기화 할 때 설명하기 위해 의도적으로 x를 var로 사용했습니다). 다음으로 y가 호출되면 초기화되고 마지막 ‘x’의 값이 고려되지만 이전 값은 고려되지 않습니다.
도움이 되었기를 바랍니다.
답변
게으른 val은 ” memoized (no-arg) def” 로 가장 쉽게 이해됩니다 .
데프처럼 게으른 val은 호출 될 때까지 평가되지 않습니다. 그러나 후속 호출이 저장된 값을 리턴하도록 결과가 저장됩니다. 메모 된 결과는 데이터 구조에서 val과 같은 공간을 차지합니다.
다른 사람들이 언급했듯이, 게으른 값의 유스 케이스는 필요할 때까지 값 비싼 계산을 연기하고 결과를 저장하고 값 사이의 특정 순환 종속성을 해결하는 것입니다.
게으른 val은 실제로 메모로 정의 된 defs로 구현됩니다. 구현에 대한 자세한 내용은 여기를 참조하십시오.
http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html
답변
lazy
다음 코드와 같이 순환 종속성이없는 경우 에도 유용합니다.
abstract class X {
val x: String
println ("x is "+x.length)
}
object Y extends X { val x = "Hello" }
Y
아직 초기화되지 않았 Y
으므로 액세스 하면 null 포인터 예외가 발생 x
합니다. 그러나 다음은 정상적으로 작동합니다.
abstract class X {
val x: String
println ("x is "+x.length)
}
object Y extends X { lazy val x = "Hello" }
Y
편집 : 다음도 작동합니다.
object Y extends { val x = "Hello" } with X
이것을 “초기 이니셜 라이저”라고합니다. 자세한 내용은 이 SO 질문 을 참조하십시오.
답변
lazy
위에서 정의한대로 정의시 실행과 액세스시 실행 시연 : (2.12.7 scala shell 사용)
// compiler says this is ok when it is lazy
scala> lazy val t: Int = t
t: Int = <lazy>
//however when executed, t recursively calls itself, and causes a StackOverflowError
scala> t
java.lang.StackOverflowError
...
// when the t is initialized to itself un-lazily, the compiler warns you of the recursive call
scala> val t: Int = t
<console>:12: warning: value t does nothing other than call itself recursively
val t: Int = t
답변
scala> lazy val lazyEight = {
| println("I am lazy !")
| 8
| }
lazyEight: Int = <lazy>
scala> lazyEight
I am lazy !
res1: Int = 8
- 모든 val은 객체 생성 중에 초기화됩니다
- lazy 키워드를 사용하여 처음 사용할 때까지 초기화 연기
- 주의 : 게으른 값은 최종적이지 않으므로 성능 저하가 나타날 수 있습니다.