수색…


비고

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 초기화가 완료되기 전에, 같은 오류가 발생합니다.

Sciddle-Js로 작성된 피들 (같은 동작이 적용됩니다)은 여기 에서 찾을 수 있습니다.

변수, 발 및 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 ydef 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가 선언되지 않은 경우에만 표시됩니다.

'게으른'

  1. 초기화는 계산 상으로 비싸고 val 사용은 거의 없습니다.

    lazy val tiresomeValue = {(1 to 1000000).filter(x => x % 113 == 0).sum}
    if (scala.util.Random.nextInt > 1000) {
      println(tiresomeValue)
    }
    

    tiresomeValue 는 계산하는 데 오랜 시간이 걸리며 항상 사용되는 것은 아닙니다. 이를 lazy val 하면 불필요한 계산을 줄일 수 있습니다.

  2. 주기적 종속성 해결

    인스턴스화하는 동안 동시에 선언해야하는 두 개의 객체가있는 예제를 살펴 보겠습니다.

    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'


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow