Helpex - Trao đổi & giúp đỡ Đăng nhập
283

Sự khác biệt giữa a varvalđịnh nghĩa trong Scala là gì và tại sao ngôn ngữ cần cả hai? Tại sao bạn lại chọn một valhơn một varvà ngược lại?

283 hữu ích 0 bình luận 100k xem chia sẻ
12 trả lời 12
313

Như rất nhiều người khác đã nói, đối tượng được gán cho valkhông thể được thay thế và đối tượng được gán cho một hộp var. Tuy nhiên, đối tượng cho biết có thể có trạng thái nội bộ của nó được sửa đổi. Ví dụ:

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.
  }
}

Vì vậy, mặc dù chúng tôi không thể thay đổi đối tượng được gán x, chúng tôi có thể thay đổi trạng thái của đối tượng đó. Tại gốc của nó, tuy nhiên, đã có một var.

Bây giờ, bất biến là một điều tốt vì nhiều lý do. Đầu tiên, nếu một đối tượng không thay đổi trạng thái bên trong, bạn không phải lo lắng nếu một phần khác trong mã của bạn thay đổi nó. Ví dụ:

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")

Điều này trở nên đặc biệt quan trọng với các hệ thống đa luồng. Trong một hệ thống đa luồng, điều sau đây có thể xảy ra:

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

Nếu bạn sử dụng valriêng và chỉ sử dụng các cấu trúc dữ liệu bất biến (nghĩa là tránh mảng, mọi thứ trong scala.collection.mutable, v.v.), bạn có thể yên tâm điều này sẽ không xảy ra. Đó là, trừ khi có một số mã, thậm chí là một khung, thực hiện các thủ thuật phản chiếu - sự phản chiếu có thể thay đổi các giá trị "bất biến", thật không may.

Đó là một lý do, nhưng có một lý do khác cho nó. Khi bạn sử dụng var, bạn có thể bị cám dỗ sử dụng lại cho cùng một varmục đích. Điều này có một số vấn đề:

  • Mọi người sẽ đọc mã sẽ khó khăn hơn để biết giá trị của biến trong một phần nhất định của mã là gì.
  • Bạn có thể quên khởi tạo lại biến trong một số đường dẫn mã và cuối cùng chuyển các giá trị sai xuống dòng trong mã.

Nói một cách đơn giản, sử dụng valsẽ an toàn hơn và dẫn đến mã dễ đọc hơn.

Chúng ta có thể, sau đó, đi theo hướng khác. Nếu valđiều đó là tốt hơn, tại sao có vartất cả? Chà, một số ngôn ngữ đã đi theo con đường đó, nhưng có nhiều tình huống trong đó tính đột biến cải thiện hiệu suất, rất nhiều.

Ví dụ, lấy một bất biến Queue. Khi bạn enqueuehoặc dequeuenhững thứ trong đó, bạn nhận được một Queueđối tượng mới . Sau đó, bạn sẽ xử lý tất cả các mục trong đó như thế nào?

Tôi sẽ trải qua điều đó với một ví dụ. Giả sử bạn có một hàng chữ số và bạn muốn soạn một số trong số chúng. Ví dụ: nếu tôi có một hàng đợi với 2, 1, 3, theo thứ tự đó, tôi muốn lấy lại số 213. Trước tiên, hãy giải quyết nó bằng mutable.Queue:

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

Mã này nhanh và dễ hiểu. Hạn chế chính của nó là hàng đợi được thông qua đã được sửa đổi toNum, vì vậy bạn phải tạo một bản sao của nó trước đó. Đó là kiểu quản lý đối tượng mà tính bất biến giúp bạn thoát khỏi.

Bây giờ, hãy chuyển đổi nó thành 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)
}

Bởi vì tôi không thể sử dụng lại một số biến để theo dõi num, như trong ví dụ trước, tôi cần phải dùng đến đệ quy. Trong trường hợp này, nó là một đệ quy đuôi, có hiệu suất khá tốt. Nhưng điều đó không phải lúc nào cũng đúng: đôi khi không có giải pháp đệ quy đuôi tốt (dễ đọc, đơn giản).

Tuy nhiên, lưu ý rằng tôi có thể viết lại mã đó để sử dụng cùng lúc immutable.Queuevà một var! Ví dụ:

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
}

Mã này vẫn hiệu quả, không yêu cầu đệ quy và bạn không cần lo lắng liệu bạn có phải tạo một bản sao của hàng đợi hay không trước khi gọi toNum. Đương nhiên, tôi tránh sử dụng lại các biến cho các mục đích khác và không có mã nào ngoài hàm này nhìn thấy chúng, vì vậy tôi không cần phải lo lắng về việc giá trị của chúng thay đổi từ dòng này sang dòng tiếp theo - ngoại trừ khi tôi rõ ràng làm như vậy.

Scala đã chọn để cho lập trình viên làm điều đó, nếu lập trình viên coi đó là giải pháp tốt nhất. Các ngôn ngữ khác đã chọn để làm cho mã như vậy khó khăn. Cái giá mà Scala (và bất kỳ ngôn ngữ nào có khả năng biến đổi rộng rãi) phải trả là trình biên dịch không mất nhiều thời gian trong việc tối ưu hóa mã như có thể. Câu trả lời của Java cho việc đó là tối ưu hóa mã dựa trên cấu hình thời gian chạy. Chúng ta có thể tiếp tục và về những ưu và nhược điểm cho mỗi bên.

Cá nhân, tôi nghĩ bây giờ Scala đạt được sự cân bằng phù hợp. Nó không hoàn hảo, cho đến nay. Tôi nghĩ rằng cả ClojureHaskell đều có những khái niệm rất thú vị không được Scala chấp nhận, nhưng Scala cũng có những thế mạnh riêng. Chúng ta sẽ thấy những gì sẽ xảy ra trong tương lai.

313 hữu ích 5 bình luận chia sẻ
57

vallà cuối cùng, nghĩa là không thể được thiết lập. Hãy suy nghĩ finaltrong java.

57 hữu ích 5 bình luận chia sẻ
44

Nói một cách đơn giản:

var = var iable

val = v khả thi + vây al

44 hữu ích 0 bình luận chia sẻ
20

Sự khác biệt là a varcó thể được gán lại trong khi valkhông thể. Khả năng biến đổi, hay nói cách khác là bất cứ điều gì thực sự được chỉ định, là một vấn đề phụ:

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.

Trong khi:

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

Và do đó:

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

Nếu bạn đang xây dựng một cấu trúc dữ liệu và tất cả các trường của nó là vals, thì cấu trúc dữ liệu đó là bất biến, vì trạng thái của nó không thể thay đổi.

20 hữu ích 3 bình luận chia sẻ
19

valcó nghĩa là bất biến và varcó nghĩa là đột biến.

Thảo luận đầy đủ.

19 hữu ích 2 bình luận chia sẻ
12

Suy nghĩ về C ++,

val x: T

tương tự như con trỏ không đổi với dữ liệu không đổi

T* const x;

trong khi

var x: T 

tương tự với con trỏ không liên tục đến dữ liệu không liên tục

T* x;

Ưu valhơn varlàm tăng tính bất biến của codebase mà có thể tạo điều kiện cho tính đúng đắn của nó, đồng thời và dễ hiểu.

12 hữu ích 2 bình luận chia sẻ
8

"val có nghĩa là bất biến và var có nghĩa là có thể thay đổi."

Để diễn giải, "val có nghĩa là giá trị và var có nghĩa là biến".

Một sự khác biệt cực kỳ quan trọng trong điện toán (bởi vì hai khái niệm này xác định chính bản chất của lập trình là gì) và OO đã làm mờ gần như hoàn toàn, bởi vì trong OO, tiên đề duy nhất là "mọi thứ đều là một vật". Và do đó, rất nhiều lập trình viên ngày nay có xu hướng không hiểu / đánh giá cao / công nhận, bởi vì họ đã bị tẩy não để "nghĩ theo cách OO" độc quyền. Thường dẫn đến các đối tượng biến / biến đổi được sử dụng như mọi nơi , khi các đối tượng giá trị / bất biến có thể / thường sẽ tốt hơn.

8 hữu ích 1 bình luận chia sẻ
6

val có nghĩa là bất biến và var có nghĩa là đột biến

bạn có thể nghĩ valnhư ngôn ngữ lập trình java finalthế giới chính hoặc c ++ ngôn ngữ consttrên thế giới chủ chốt.

6 hữu ích 0 bình luận chia sẻ
1

Nó đơn giản như tên của nó.

var có nghĩa là nó có thể thay đổi

val có nghĩa là bất biến

1 hữu ích 0 bình luận chia sẻ
0

Val - giá trị là hằng số lưu trữ gõ. Sau khi tạo giá trị của nó không thể được gán lại. một giá trị mới có thể được xác định với val từ khóa.

ví dụ. val x: Int = 5

Ở đây loại là tùy chọn vì scala có thể suy ra nó từ giá trị được gán.

Biến - biến là các đơn vị lưu trữ được nhập có thể được gán lại giá trị miễn là không gian bộ nhớ được dành riêng.

ví dụ. var x: Int = 5

Dữ liệu được lưu trữ trong cả hai đơn vị lưu trữ được JVM phân bổ tự động một khi chúng không còn cần thiết nữa.

Trong các giá trị scala được ưu tiên hơn các biến do tính ổn định, các giá trị này mang đến mã đặc biệt là mã đồng thời và đa luồng.

0 hữu ích 0 bình luận chia sẻ
0

Mặc dù nhiều người đã trả lời sự khác biệt giữa Valvar . Nhưng một điểm cần chú ý là val không chính xác như từ khóa cuối cùng .

Chúng ta có thể thay đổi giá trị của val bằng cách sử dụng đệ quy nhưng chúng ta không bao giờ có thể thay đổi giá trị cuối cùng. Chung kết là không đổi hơn Val.

def factorial(num: Int): Int = {
 if(num == 0) 1
 else factorial(num - 1) * num
}

Các tham số phương thức theo mặc định val và tại mỗi giá trị cuộc gọi đang được thay đổi.

0 hữu ích 0 bình luận chia sẻ
0

Valcó nghĩa là cuối cùng của nó , không thể được chỉ định lại

Trong khi đó, Varcó thể được chỉ định lại sau .

0 hữu ích 1 bình luận chia sẻ
loading
Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ scala , hoặc hỏi câu hỏi của bạn.

Có thể bạn quan tâm

loading