답변:
꽤 일반적인 질문입니다. 어려운 것은 중복을 찾는 것입니다.
참조 투명성을 위해 노력해야합니다 . 즉, "e"라는 표현이 있으면를 만들고으로 val x = e바꿀 수 e있습니다 x. 이것은 가변성이 깨지는 속성입니다. 디자인 결정을 내려야 할 때마다 참조 투명성을 최대화하십시오.
실질적으로 메서드 로컬 var은 메서드를 벗어나지 var않기 때문에 존재 하는 가장 안전 합니다. 방법이 짧으면 더 좋습니다. 그렇지 않은 경우 다른 방법을 추출하여 줄이십시오.
반면에 변경 가능한 컬렉션은 그렇지 않더라도 탈출 할 가능성 이 있습니다. 코드를 변경할 때 다른 메서드에 전달하거나 반환 할 수 있습니다. 그것은 참조 투명성을 깨뜨리는 종류의 것입니다.
객체 (필드)에서도 거의 똑같은 일이 발생하지만 더 심각한 결과가 발생합니다. 어느 쪽이든 객체는 상태를 가지므로 참조 투명성이 깨집니다. 그러나 변경 가능한 컬렉션이 있다는 것은 객체 자체조차도 누가 변경하는지 제어 할 수 없다는 것을 의미합니다.
immutable val이상 immutable var이상 mutable val이상 mutable var. 특히 immutable var끝났습니다 mutable val!
var. 변경 불가능한 컬렉션을 사용하는 또 다른 좋은 기능은 var변경된 경우에도 오래된 복사본을 효율적으로 보관할 수 있다는 것 입니다.
var x: Set[Int] 보다 선호 합니다. 전자의 경우에는 해당 함수가 사용자를 위해 변경 될 수 없습니다 . val x: mutable.Set[Int]xx
변경 불가능한 컬렉션으로 작업하고이를 "수정"해야하는 경우 (예 : 루프에 요소를 추가하는 var경우) 결과 컬렉션을 어딘가에 저장해야하므로 s 를 사용해야 합니다. 변경 불가능한 컬렉션에서만 읽는 경우 vals 를 사용하십시오 .
일반적으로 참조와 객체를 혼동하지 않도록하십시오. vals는 불변 참조 (C의 상수 포인터)입니다. 당신이 사용하는 경우 즉, val x = new MutableFoo()당신은 할 수 있습니다 개체 변경 이 x포인트를,하지만 당신은 할 수 없습니다 객체로 변경하는 x 점. 를 사용하면 반대가 유지됩니다 var x = new ImmutableFoo(). 내 초기 조언을 따름 : 참조가 가리키는 객체를 변경할 필요가 없으면 vals를 사용하십시오 .
var immutable = something(); immutable = immutable.update(x)불변 컬렉션을 사용하는 목적을 무효화합니다. 이미 참조 투명성을 포기했으며 일반적으로 더 나은 시간 복잡성으로 변경 가능한 컬렉션에서 동일한 효과를 얻을 수 있습니다. 네 가지 가능성 ( val및 var, 변경 가능 및 불변) 중 이것은 가장 의미가 없습니다. 나는 자주 사용 val mutable합니다.
var list: List[X] = Nil; list = item :: list; ...내가 한때 다르게 쓴 것을 잊었다.
이에 답하는 가장 좋은 방법은 예를 사용하는 것입니다. 어떤 이유로 단순히 숫자를 수집하는 프로세스가 있다고 가정합니다. 이 번호를 기록하고 이를 수행 하기 위해 컬렉션을 다른 프로세스로 보냅니다 .
물론 수집 물을 로거로 보낸 후에도 여전히 숫자를 수집하고 있습니다. 그리고 실제 로깅을 지연시키는 로깅 프로세스에 약간의 오버 헤드가 있다고 가정 해 보겠습니다. 이것이 어디로 가는지 알 수 있기를 바랍니다.
이 컬렉션을 mutable에 저장하면 val(계속 추가하고 있기 때문에 변경 가능) 이는 로깅을 수행하는 프로세스가 컬렉션 프로세스에 의해 여전히 업데이트되고 있는 동일한 객체 를 보게 될 것임을 의미합니다 . 해당 컬렉션은 언제든지 업데이트 될 수 있으므로 로깅 할 때 실제로 보낸 컬렉션을 로깅하지 않을 수 있습니다.
immutable을 사용하는 경우 var변경 불가능한 데이터 구조를 로거에 보냅니다. 우리가 우리의 컬렉션에 더 많은 번호를 추가 할 때, 우리는 것입니다 대체 우리를 var로모그래퍼 새로운 불변의 데이터 구조 . 이것은 로거로 전송 된 컬렉션이 교체된다는 의미가 아닙니다! 전송 된 컬렉션을 여전히 참조하고 있습니다. 따라서 로거는 실제로받은 컬렉션을 기록합니다.
동시성 시나리오에서 사용할 콤보에 대한 질문 인 동시성에 대한 불변성의 중요성에 대한 질문이 더욱 중요해 지므로이 블로그 게시물의 예제가 더 많은 빛을 발할 것이라고 생각합니다 . 그리고 우리가 그 과정에서 AtomicReference와 같은 것보다 동기화 된 것과 @ 휘발성을 선호하는 용도에 주목하십시오 : 세 가지 도구
var immutable 대 val mutable
이 질문에 대한 많은 훌륭한 답변 외에도. 다음은 잠재적 인 위험을 보여주는 간단한 예입니다 val mutable.
변경 가능한 개체는 메서드 내에서 수정할 수 있으며,이를 매개 변수로 사용하지만 재 할당은 허용되지 않습니다.
import scala.collection.mutable.ArrayBuffer
object MyObject {
def main(args: Array[String]) {
val a = ArrayBuffer(1,2,3,4)
silly(a)
println(a) // a has been modified here
}
def silly(a: ArrayBuffer[Int]): Unit = {
a += 10
println(s"length: ${a.length}")
}
}
결과:
length: 5
ArrayBuffer(1, 2, 3, 4, 10)
var immutable재 할당이 허용되지 않기 때문에 에서는 이와 같은 일이 발생할 수 없습니다 .
object MyObject {
def main(args: Array[String]) {
var v = Vector(1,2,3,4)
silly(v)
println(v)
}
def silly(v: Vector[Int]): Unit = {
v = v :+ 10 // This line is not valid
println(s"length of v: ${v.length}")
}
}
결과 :
error: reassignment to val
함수 매개 변수는 val이 재 할당이 허용되지 않는 것으로 취급되기 때문에 .
mutable val이 immutable var. 여기에 무엇이 잘못 되었습니까?
+=배열 버퍼와 같은 방법 이 없습니다 . 귀하의 답변 +=은 x = x + y그것이 아닌 것과 동일 함을 의미합니다 . 함수 매개 변수가 vals로 취급된다는 귀하의 진술은 정확하고 언급 한 오류가 발생하지만 =. ArrayBuffer로 동일한 오류를 얻을 수 있으므로 여기의 컬렉션 변경 가능성은 실제로 관련이 없습니다. 따라서 OP가 말하는 것을 얻지 못하기 때문에 좋은 대답이 아닙니다. 의도하지 않은 경우 변경 가능한 컬렉션을 전달하는 위험에 대한 좋은 예입니다.
ArrayBuffer사용하여 내가 얻는 동작을 복제 할 수 없습니다 Vector. OP의 질문은 광범위하지만 언제 사용할 지에 대한 제안을 찾고 있었으므로 변경 가능한 컬렉션을 전달하는 위험을 보여주기 때문에 내 대답이 유용하다고 생각합니다 (그것이 val도움 이 되지 않습니다). immutable var보다 안전합니다 mutable val.