[scala] 스칼라에서 var와 val 정의의 차이점은 무엇입니까?

스칼라에서 a varval정의 의 차이점은 무엇 이며 왜 언어에 둘 다 필요한가요? 왜 val이상 을 선택 var하고 그 반대를 선택 하시겠습니까?



답변

다른 많은 사람들이 말했듯이 val캔에 할당 된 객체 는 교체 할 수 없으며 캔에 할당 된 객체 는 교체 할 수 없습니다 var. 그러나, 상기 객체는 내부 상태가 수정 될 수있다. 예를 들면 다음과 같습니다.

class A(n: Int) {
  var value = n
}

class B(n: Int) {
  val value = new A(n)
}

object Test {
  def main(args: Array[String]) {
    val x = new B(5)
    x = new B(6) // Doesn't work, because I can't replace the object created on the line above with this new one.
    x.value = new A(6) // Doesn't work, because I can't replace the object assigned to B.value for a new one.
    x.value.value = 6 // Works, because A.value can receive a new object.
  }
}

따라서에 할당 된 객체를 변경할 수는 없지만 해당 객체 x의 상태를 변경할 수 있습니다. 그러나 그 근본에는 var.

불변성은 여러 가지 이유로 좋은 것입니다. 첫째, 객체가 내부 상태를 변경하지 않아도 코드의 다른 부분이 변경되는지 걱정할 필요가 없습니다. 예를 들면 다음과 같습니다.

x = new B(0)
f(x)
if (x.value.value == 0)
  println("f didn't do anything to x")
else
  println("f did something to x")

이것은 다중 스레드 시스템에서 특히 중요합니다. 멀티 스레드 시스템에서 다음이 발생할 수 있습니다.

x = new B(1)
f(x)
if (x.value.value == 1) {
  print(x.value.value) // Can be different than 1!
}

당신이 사용하는 경우 val에만, 오직 불변의 데이터 구조 (즉, 피할 어레이의 모든 사용 scala.collection.mutable등), 당신은 이런 일이되지 않습니다 안심 할 수 있습니다. 즉, 리플렉션 트릭을 수행하는 코드가 있거나 프레임 워크가없는 경우 리플렉션은 “불변”값을 변경할 수 있습니다.

한 가지 이유가 있지만 또 다른 이유가 있습니다. 를 사용하면 여러 용도로 var동일한 것을 재사용하려는 유혹을받을 수 있습니다 var. 여기에는 몇 가지 문제가 있습니다.

  • 코드를 읽는 사람들은 코드의 특정 부분에서 변수의 값이 무엇인지 알기가 더 어려울 것입니다.
  • 일부 코드 경로에서 변수를 다시 초기화하고 코드에서 잘못된 값을 다운 스트림으로 전달하는 것을 잊을 수 있습니다.

간단히 말해, 사용하는 val것이 더 안전하고 더 읽기 쉬운 코드로 이어집니다.

그러면 우리는 다른 방향으로 갈 수 있습니다. val그것이 더 나은 경우 , 왜 var전혀 있습니까? 글쎄, 일부 언어는 그 길을 택했지만 가변성이 성능을 향상시키는 상황이 많이 있습니다.

예를 들어, 불변을 가져옵니다 Queue. 당신이 enqueue또는 그 dequeue안에있을 때, 당신은 새로운 Queue물건 을 얻습니다 . 그렇다면 모든 항목을 처리하는 방법은 무엇입니까?

예제를 통해 살펴 보겠습니다. 숫자 줄이 있고 숫자를 작성한다고 가정 해 봅시다. 예를 들어, 순서대로 2, 1, 3의 대기열이 있으면 숫자 213을 다시 얻고 싶습니다. 먼저 다음과 같이 해결하십시오 mutable.Queue.

def toNum(q: scala.collection.mutable.Queue[Int]) = {
  var num = 0
  while (!q.isEmpty) {
    num *= 10
    num += q.dequeue
  }
  num
}

이 코드는 빠르고 이해하기 쉽습니다. 주요 결점은 전달 된 대기열이에 의해 수정 toNum되므로 미리 사본을 만들어야한다는 것입니다. 그것은 불변성이 당신을 자유롭게하는 종류의 객체 관리입니다.

자, 그것을 다음과 같이 보자 immutable.Queue.

def toNum(q: scala.collection.immutable.Queue[Int]) = {
  def recurse(qr: scala.collection.immutable.Queue[Int], num: Int): Int = {
    if (qr.isEmpty)
      num
    else {
      val (digit, newQ) = qr.dequeue
      recurse(newQ, num * 10 + digit)
    }
  }
  recurse(q, 0)
}

num이전 예제 에서처럼 내 변수를 추적하기 위해 일부 변수를 재사용 할 수 없기 때문에 재귀를 사용해야합니다. 이 경우에는 꼬리 재귀이며 성능이 매우 좋습니다. 그러나 항상 그런 것은 아닙니다. 때로는 좋은 (읽기 쉽고 간단한) 꼬리 재귀 솔루션이 없습니다.

단, 내가 사용하는 코드를 다시 작성할 수있는 immutable.Queue과를 var동시에를! 예를 들면 다음과 같습니다.

def toNum(q: scala.collection.immutable.Queue[Int]) = {
  var qr = q
  var num = 0
  while (!qr.isEmpty) {
    val (digit, newQ) = qr.dequeue
    num *= 10
    num += digit
    qr = newQ
  }
  num
}

이 코드는 여전히 효율적이며 재귀를 요구하지 않으며을 호출하기 전에 큐 사본을 만들어야하는지 걱정할 필요가 없습니다 toNum. 당연히 다른 목적으로 변수를 재사용하는 것을 피 했으며이 함수 외부의 코드에서는 변수를 볼 수 없으므로 명시 적으로 수행하지 않는 한 값이 한 줄에서 다음 줄로 변경되는 것에 대해 걱정할 필요가 없습니다.

스칼라는 프로그래머가 최선의 해결책이라고 생각하면 프로그래머가 그렇게 할 수 있도록 선택했다. 다른 언어들은 그러한 코드를 어렵게 만들었습니다. 스칼라 (및 광범위한 가변성을 가진 모든 언어)가 지불하는 가격은 컴파일러가 코드를 최적화하는 데 많은 여유가 없다는 것입니다. 이에 대한 Java의 대답은 런타임 프로파일을 기반으로 코드를 최적화하는 것입니다. 우리는 양측의 장단점에 대해 계속 갈 수있었습니다.

개인적으로, 스칼라는 지금 당장 균형을 맞추고 있다고 생각합니다. 완벽하지는 않다. ClojureHaskell 은 스칼라에서 채택하지 않은 매우 흥미로운 개념을 가지고 있다고 생각 하지만 스칼라는 자체 강점도 있습니다. 우리는 미래에 무슨 일이 일어날 지 보게 될 것입니다.


답변

val최종, 즉 설정할 수 없습니다. final자바로 생각하십시오 .


답변

간단히 말해서 :

var = var iable

val = v ariable + fin al


답변

차이점은을 var다시 할당 할 수 있지만을 val할 수는 없다는 것입니다. 변경 가능성 또는 실제로 할당 된 것 중 하나는 부수적 인 문제입니다.

import collection.immutable
import collection.mutable
var m = immutable.Set("London", "Paris")
m = immutable.Set("New York") //Reassignment - I have change the "value" at m.

이므로:

val n = immutable.Set("London", "Paris")
n = immutable.Set("New York") //Will not compile as n is a val.

따라서 :

val n = mutable.Set("London", "Paris")
n = mutable.Set("New York") //Will not compile, even though the type of n is mutable.

데이터 구조를 작성 중이고 모든 필드가 vals 인 경우 상태가 변경 될 수 없으므로 해당 데이터 구조는 변경할 수 없습니다.


답변

val불변의 var것을 의미하고 변경 가능한 것을 의미합니다.

전체 토론.


답변

C ++의 관점에서 생각하면

val x: T

일정하지 않은 데이터에 대한 지속적인 포인터와 유사

T* const x;

동안

var x: T 

상수가 아닌 데이터에 대한 상수가 아닌 포인터와 유사

T* x;

우선권 valvar두면 코드베이스의 불변성이 증가하여 정확성, 동시성 및 이해가 용이 해집니다.

상수가 아닌 데이터에 대한 지속적인 포인터의 의미를 이해하려면 다음 스칼라 스 니펫을 고려하십시오.

val m = scala.collection.mutable.Map(1 -> "picard")
m // res0: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard)

여기에서 “포인터” val m 는 일정하므로 다른 것을 가리 키도록 다시 할당 할 수 없습니다

m = n // error: reassignment to val

것을 그러나 우리는 참으로 일정하지 않은 데이터 자체를 변경할 수 있습니다 m포인트가 너무 좋아

m.put(2, "worf")
m // res1: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard, 2 -> worf)


답변

“val은 변경 불가능 함을 의미하고 var는 변경 가능함을 의미합니다.”

바꾸어 말하면, “val은 값을 의미하고 var는 변수를 의미합니다”.

컴퓨팅에서 매우 중요한 차이 (이 두 개념이 프로그래밍의 핵심 요소를 정의하기 때문에)와 OO가 거의 완전히 흐릿하게 관리되었다는 점은 OO에서 유일한 원칙은 “모든 것이 목적”. 그리고 그 결과 오늘날 많은 프로그래머들이 독점적으로 “OO 방식을 생각”하기 위해 세뇌되어 왔기 때문에 이해 / 감사 / 인식하지 않는 경향이 있습니다. 가치 / 불변의 객체가 종종 더 좋았을 수도있는 가변 / 변동 가능한 객체가 어디에서나 사용되는 경우가 종종 있습니다.