변경 가능한 컬렉션에서 val을 사용하는 경우와 변경 불가능한 컬렉션에서 var를 사용하는 경우에 대한 Scala의 지침이 있습니까? 아니면 정말 불변의 컬렉션으로 val을 목표로해야할까요?
두 가지 유형의 컬렉션이 모두 있다는 사실은 저에게 많은 선택권을 제공하며 종종 그 선택을하는 방법을 모릅니다.
답변
꽤 일반적인 질문입니다. 어려운 것은 중복을 찾는 것입니다.
참조 투명성을 위해 노력해야합니다 . 즉, “e”라는 표현이 있으면를 만들고으로 val x = e
바꿀 수 e
있습니다 x
. 이것은 가변성이 깨지는 속성입니다. 디자인 결정을 내려야 할 때마다 참조 투명성을 최대화하십시오.
실질적으로 메서드 로컬 var
은 메서드를 벗어나지 var
않기 때문에 존재 하는 가장 안전 합니다. 방법이 짧으면 더 좋습니다. 그렇지 않은 경우 다른 방법을 추출하여 줄이십시오.
반면에 변경 가능한 컬렉션은 그렇지 않더라도 탈출 할 가능성 이 있습니다. 코드를 변경할 때 다른 메서드에 전달하거나 반환 할 수 있습니다. 그것은 참조 투명성을 깨뜨리는 종류의 것입니다.
객체 (필드)에서도 거의 똑같은 일이 발생하지만 더 심각한 결과가 발생합니다. 어느 쪽이든 객체는 상태를 가지므로 참조 투명성이 깨집니다. 그러나 변경 가능한 컬렉션이 있다는 것은 객체 자체조차도 누가 변경하는지 제어 할 수 없다는 것을 의미합니다.
답변
변경 불가능한 컬렉션으로 작업하고이를 “수정”해야하는 경우 (예 : 루프에 요소를 추가하는 var
경우) 결과 컬렉션을 어딘가에 저장해야하므로 s 를 사용해야 합니다. 변경 불가능한 컬렉션에서만 읽는 경우 val
s 를 사용하십시오 .
일반적으로 참조와 객체를 혼동하지 않도록하십시오. val
s는 불변 참조 (C의 상수 포인터)입니다. 당신이 사용하는 경우 즉, val x = new MutableFoo()
당신은 할 수 있습니다 개체 변경 이 x
포인트를,하지만 당신은 할 수 없습니다 객체로 변경하는 x
점. 를 사용하면 반대가 유지됩니다 var x = new ImmutableFoo()
. 내 초기 조언을 따름 : 참조가 가리키는 객체를 변경할 필요가 없으면 val
s를 사용하십시오 .
답변
이에 답하는 가장 좋은 방법은 예를 사용하는 것입니다. 어떤 이유로 단순히 숫자를 수집하는 프로세스가 있다고 가정합니다. 이 번호를 기록하고 이를 수행 하기 위해 컬렉션을 다른 프로세스로 보냅니다 .
물론 수집 물을 로거로 보낸 후에도 여전히 숫자를 수집하고 있습니다. 그리고 실제 로깅을 지연시키는 로깅 프로세스에 약간의 오버 헤드가 있다고 가정 해 보겠습니다. 이것이 어디로 가는지 알 수 있기를 바랍니다.
이 컬렉션을 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
이 재 할당이 허용되지 않는 것으로 취급되기 때문에 .