Scala Language
변수, 발 및 Def
수색…
비고
val
은 의미 상으로 정적이므로 코드에 나타날 때마다 "내부에서"초기화됩니다. 이것은 추상 클래스 및 특성에서 사용될 때 놀라운 바람직하지 않은 동작을 생성 할 수 있습니다.
예를 들어, 래핑 된 Int
에서 증가 작업을 정의하는 PlusOne
이라는 특성을 만들고 싶다고 가정 해 보겠습니다. Int
는 불변이므로, 초기화에 + 값을 더한 값이 알려지며 나중에 변경되지 않으므로 의미 상으로 val
됩니다. 그러나이 방법으로 정의하면 예기치 않은 결과가 발생합니다.
trait PlusOne {
val i:Int
val incr = i + 1
}
class IntWrapper(val i: Int) extends PlusOne
상관없이 어떤 값을 i
당신이 구성 IntWrapper
전화로 .incr
항상 1을 반환합니다 반환 된 객체에 이쪽 발에 있기 때문이다 incr
연장 수업 전에 특성에 초기화되며, 그 시간에 i
단지의 기본 값이 0
. (다른 조건에서는 Nil
, null
또는 유사한 기본값으로 채워질 수 있습니다.)
일반적인 규칙은 추상 필드에 의존하는 모든 값에 val
을 사용하지 않는 것입니다. 대신, 필요할 때까지 평가하지 않는 lazy val
또는 호출 될 때마다 평가되는 def
. 경우 생성 것을 그러나 참고 lazy val
a로 평가 강제로 val
초기화가 완료되기 전에, 같은 오류가 발생합니다.
변수, 발 및 Def
var
var
은 Java와 같은 언어의 변수와 비슷한 참조 변수입니다. 주어진 객체가 var
가 선언 된 것과 같은 유형을 가지고있는 한, 다른 객체를 var
자유롭게 할당 할 수 있습니다 :
scala> var x = 1
x: Int = 1
scala> x = 2
x: Int = 2
scala> x = "foo bar"
<console>:12: error: type mismatch;
found : String("foo bar")
required: Int
x = "foo bar"
^
위의 예제에서 var
의 유형은 첫 번째 값 지정이 주어진 컴파일러에 의해 추론되었습니다.
발
val
은 상수 참고입니다. 따라서 이미 할당 된 val
새 객체를 할당 할 수 없습니다.
scala> val y = 1
y: Int = 1
scala> y = 2
<console>:12: error: reassignment to val
y = 2
^
그러나 val
가리키는 객체는 일정 하지 않습니다 . 해당 객체는 수정 될 수 있습니다.
scala> val arr = new Array[Int](2)
arr: Array[Int] = Array(0, 0)
scala> arr(0) = 1
scala> arr
res1: Array[Int] = Array(1, 0)
def
def
는 메소드를 정의합니다. 메소드를 다시 할당 할 수 없습니다.
scala> def z = 1
z: Int
scala> z = 2
<console>:12: error: value z_= is not a member of object $iw
z = 2
^
위의 예제에서 val y
와 def z
는 같은 값을 반환합니다. 그러나 def
는 호출 될 때 평가되고 val
또는 var
은 할당 될 때 평가 됩니다 . 정의에 부작용이있을 때이 동작이 달라질 수 있습니다.
scala> val a = {println("Hi"); 1}
Hi
a: Int = 1
scala> def b = {println("Hi"); 1}
b: Int
scala> a + 1
res2: Int = 2
scala> b + 1
Hi
res3: Int = 2
기능들
함수는 값이기 때문에 val
/ var
/ def
할당 할 수 있습니다. 다른 모든 것은 위와 같은 방식으로 작동합니다.
scala> val x = (x: Int) => s"value=$x"
x: Int => String = <function1>
scala> var y = (x: Int) => s"value=$x"
y: Int => String = <function1>
scala> def z = (x: Int) => s"value=$x"
z: Int => String
scala> x(1)
res0: String = value=1
scala> y(2)
res1: String = value=2
scala> z(3)
res2: String = value=3
게으른 발
lazy val
a의 초기화 언어 기능입니다 val
그것이 처음으로 액세스 될 때까지 지연됩니다. 그 시점 이후, 그것은 보통의 val
처럼 행동합니다.
이것을 사용하려면 val
앞에 lazy
키워드를 추가하십시오. 예를 들어 REPL :
scala> lazy val foo = {
| println("Initializing")
| "my foo value"
| }
foo: String = <lazy>
scala> val bar = {
| println("Initializing bar")
| "my bar value"
| }
Initializing bar
bar: String = my bar value
scala> foo
Initializing
res3: String = my foo value
scala> bar
res4: String = my bar value
scala> foo
res5: String = my foo value
이 예제는 실행 순서를 보여줍니다. lazy val
이 선언되면 foo
값에 저장되는 것은 아직 평가되지 않은 지연 함수 호출입니다. 일반 val
이 설정되면 println
호출이 실행되고 bar
값이 할당됩니다. 처음으로 println
실행되는 것을 보았을 때 foo
평가할 때 - 그러나 두 번째로 평가 될 때는 그렇지 않습니다. 마찬가지로 bar
가 평가 될 때 println
execute가 선언되지 않은 경우에만 표시됩니다.
'게으른'
초기화는 계산 상으로 비싸고
val
사용은 거의 없습니다.lazy val tiresomeValue = {(1 to 1000000).filter(x => x % 113 == 0).sum} if (scala.util.Random.nextInt > 1000) { println(tiresomeValue) }
tiresomeValue
는 계산하는 데 오랜 시간이 걸리며 항상 사용되는 것은 아닙니다. 이를lazy val
하면 불필요한 계산을 줄일 수 있습니다.주기적 종속성 해결
인스턴스화하는 동안 동시에 선언해야하는 두 개의 객체가있는 예제를 살펴 보겠습니다.
object comicBook { def main(args:Array[String]): Unit = { gotham.hero.talk() gotham.villain.talk() } } class Superhero(val name: String) { lazy val toLockUp = gotham.villain def talk(): Unit = { println(s"I won't let you win ${toLockUp.name}!") } } class Supervillain(val name: String) { lazy val toKill = gotham.hero def talk(): Unit = { println(s"Let me loosen up Gotham a little bit ${toKill.name}!") } } object gotham { val hero: Superhero = new Superhero("Batman") val villain: Supervillain = new Supervillain("Joker") }
lazy
키워드가 없으면 각 객체는 객체의 멤버가 될 수 없습니다. 그러한 프로그램을 실행하면java.lang.NullPointerException
합니다.lazy
를 사용함으로써 초기화되지 않은 값을 가질 염려없이 레퍼런스를 초기화하기 전에 할당 할 수 있습니다.
오버로드 데프
서명이 다른 경우 def
를 오버로드 할 수 있습니다.
def printValue(x: Int) {
println(s"My value is an integer equal to $x")
}
def printValue(x: String) {
println(s"My value is a string equal to '$x'")
}
printValue(1) // prints "My value is an integer equal to 1"
printValue("1") // prints "My value is a string equal to '1'"
이것은 내부 클래스, 특성, 객체 여부와 상관없이 동일하게 작동합니다.
명명 된 매개 변수
def
를 호출 할 때, 매개 변수는 명시 적으로 이름으로 할당 될 수 있습니다. 그렇게하는 것은 그들이 올바르게 주문할 필요가 없음을 의미합니다. 예를 들어, printUs()
를 다음과 같이 정의 printUs()
.
// print out the three arguments in order.
def printUs(one: String, two: String, three: String) =
println(s"$one, $two, $three")
이제는 다음과 같은 방식으로 호출 될 수 있습니다 (다른 것들 사이에서).
printUs("one", "two", "three")
printUs(one="one", two="two", three="three")
printUs("one", two="two", three="three")
printUs(three="three", one="one", two="two")
모든 경우에 one, two, three
이 인쇄됩니다.
모든 인수가 명명되지 않은 경우 첫 x 째 인수는 주.으로 대응됩니다. 명명 된 인수 다음에 위치 지정되지 않은 인수가있을 수 없습니다.
printUs("one", two="two", three="three") // prints 'one, two, three'
printUs(two="two", three="three", "one") // fails to compile: 'positional after named argument'