2010-10-26 4 views
22

Tại sao không cho nó có thể có điều này:Scala gán Vals

def main(args:Array[String]) { 
    val whatever:String // Have it uninitialized here 

    if(someCondition) { 
     whatever = "final value" // Initialize it here 
    } 
} 

Tôi không hiểu tại sao điều này không nên hợp pháp. Tôi biết rằng tôi có thể làm cho nó là var, nhưng tại sao chúng ta phải khởi tạo chính xác val khi chúng tôi khai báo? Nó có vẻ hợp lý hơn để có thể khởi tạo nó sau này?

+0

Trình biên dịch đảm bảo rằng nó thực sự được intialized như thế nào? Có, nó có thể - nhưng khó, và không cần thiết. – delnan

+2

Vì đến thời điểm khởi tạo đó, giá trị là * không xác định * và chỉ có ý nghĩa về dòng điều khiển sau. Trong lập trình hàm, không có giá trị không xác định - và các giá trị - không phải là trạng thái - là những thứ duy nhất quan trọng. – Dario

+0

Vâng, về cơ bản không có "uninitialized". JVM không hỗ trợ khái niệm này. Trong Java, các trường "uninitialized" có giá trị 'null'. Bởi vì cách khởi tạo lớp hoạt động, đôi khi có thể truy cập các trường và nhận được các giá trị rỗng không có thật. Điều này dẫn đến mã có vẻ không tốt, nhưng ném 'NPE' vào lúc chạy. Serialization/RMI thêm nhiều vấn đề hơn cho cách tiếp cận đó. Nếu bạn quan tâm, hãy xem một số ví dụ trong "Java Puzzlers". Về cơ bản: Scala ngăn chặn những điều kỳ lạ xảy ra và làm giảm sự cần thiết cho các giá trị uninitialized, bởi vì tất cả mọi thứ là một biểu thức. – soc

Trả lời

38

Bạn có thể làm:

val whatever = 
    if (someCondition) 
     "final value" 
    else 
     "other value" 
+1

Và nếu someCondition cần được sử dụng để đặt nhiều giá trị, bạn có thể sử dụng các bộ dữ liệu và phân tách giá trị. – andresp

3

Bởi vì mục đích của 'val' là để báo hiệu cho người đọc (và trình biên dịch): "Giá trị này sẽ ở lại những gì nó được khởi tạo cho đến khi nó đi ra khỏi sự tồn tại "

Điều này không có ý nghĩa nhiều nếu không khởi tạo.

Tất nhiên người ta có thể mơ ước một số thứ như val (3) cho phép ba gán cho một biến, nhưng tôi không nghĩ rằng đó sẽ được sử dụng nhiều.

9

Sử dụng lazy val giống như vậy:

def main(args:Array[String]) { 
    lazy val whatever:String = if (someCondition) "final value" else "other value" 

    // ... 

    println(whatever) // will only initialize on first access 
} 
+1

Nhiệm vụ bên trong nếu sẽ trả lại Đơn vị. Một nếu không có mệnh đề khác cũng sẽ trả về Đơn vị. Tôi nghĩ rằng ví dụ của bạn sẽ không biên dịch ... –

+0

Đọc trước khi gửi ... Bạn nói đúng. Đã sửa. –

7

Bên cạnh đó với những gì người khác đã nói, lưu ý rằng Java cho phép "trống cuối cùng" "biến", mà là một tính năng tôi kinda bỏ lỡ:

final Boolean isItChristmasYet; 

if (new Date().before(christmas)) { 
    isItChristmasYet = Boolean.FALSE; 
} else { 
    isItChristmasYet = Boolean.TRUE; 
} 

Tuy nhiên, nhờ phân tích lưu lượng dữ liệu trong trình biên dịch, javac sẽ không cho phép bạn bỏ biến số whatever chưa được gán nếu someCondition không giữ.

giải pháp
+0

Vâng, đây cũng là điều tôi nhớ từ Java. – Geo

+1

Trong sự tò mò, chính xác những gì bạn đạt được bằng cách có biến chưa được khai báo trước được khai báo? Nó không phải là nếu bạn có thể sử dụng nó trong bất kỳ cách nào trước khi nó thực sự khởi tạo ... –

+0

Đó là một điều phong cách, tôi sử dụng để tuyên bố tất cả người dân địa phương của tôi lên phía trước ở đầu phạm vi của họ. Tôi có lẽ vẫn sẽ - ít nhất là đôi khi - nếu Scala có vals trống. :) –

27

Java thực sự là một cách giải quyết cho vấn đề mà không phải tất cả biểu thức trả về giá trị, vì vậy bạn không thể viết này trong Java:

final String whatever = if (someCondition) { 
    "final value" 
} else { 
    "other value" 
} 

Càng ngày, xu hướng trong Java là sử dụng các nhà điều hành ternary thay vào đó:

final String whatever = someCondition ? "final value" : "other value" 

Điều này là tốt cho trường hợp sử dụng hạn chế đó, nhưng hoàn toàn không thể chịu được khi bạn bắt đầu xử lý các câu lệnh chuyển đổi và nhiều nhà xây dựng.


Cách tiếp cận của Scala khác nhau ở đây. Tất cả việc xây dựng đối tượng cuối cùng phải đi qua một hàm khởi tạo "chính", tất cả các biểu thức trả về một giá trị (ngay cả khi nó là Unit, tương đương với Java Void), và việc xây dựng tiêm được ưu tiên mạnh. Điều này dẫn đến biểu đồ đối tượng được xây dựng một cách rõ ràng dưới dạng Đồ thị theo chu kỳ được chỉ định và cũng hoạt động rất độc đáo với các đối tượng bất biến. Bạn cũng cần lưu ý rằng việc khai báo và xác định các biến trong các hoạt động riêng biệt, nói chung là một thực hành không tốt khi xử lý nhiều luồng - và có thể khiến bạn dễ bị lộ rỗng và điều kiện chủng tộc khi bạn mong đợi chúng (mặc dù ít nhất là). điều này không thực sự là một vấn đề trong quá trình xây dựng đối tượng). Việc tạo ra nguyên tử các giá trị bất biến chỉ là một khía cạnh của cách thức mà các ngôn ngữ chức năng giúp giải quyết đồng thời.

Điều này cũng có nghĩa là trình biên dịch Scala có thể tránh được một số phân tích lưu lượng phức tạp từ đặc tả ngôn ngữ Java.

Như đã trình bày ở trên, Scala Way ™ là:

val whatever = 
    if (someCondition) 
    "final value" 
    else 
    "other value" 

Một cách tiếp cận mà cũng có quy mô lên đến cấu trúc điều khiển khác:

val whatever = someCondition match { 
    case 1 => "one" 
    case 2 => "two" 
    case 3 => "three" 
    case _ => "other" 
} 

Với một chút Scala kinh nghiệm bạn sẽ khám phá ra rằng phong cách này giúp trình biên dịch giúp bạn, và bạn sẽ thấy mình viết chương trình với ít lỗi hơn!