2012-04-15 26 views
10

Tôi đang trên tập thể dục 5.7 của "Scala cho Nóng lòng chờ đợi", nơi mà tôi cần phải tạo ra một Person lớp mà phải mất một tên : String trên constructor và có 2 thuộc tính firstNamelastName được điền từ tên được phân tách bằng khoảng trắng. thử nghiệm đầu tiên của tôi là:biến Constructor địa phương trong Scala

class Person(name:String) { 
    private val nameParts = name.split(" ") 

    val firstName = nameParts(0) 
    val lastName = nameParts(1) 
} 

Vấn đề là, mà bây giờ nameParts vẫn là một lĩnh vực tư nhân luôn có thể nhìn thấy trong lớp, trong khi thực tế chỉ nên tồn tại trong môi trường địa phương của constructor. Java tương đương với những gì tôi muốn sẽ là:

class Person{ 
    private final String firstName; 
    private final String lastName; 

    Person(String name){ 
     final String[] nameParts = name.split(" "); 
     firstName = nameParts[0]; 
     lastName = nameParts[1]; 
    } 
} 

Ở đây, nameParts tồn tại chỉ withing các nhà xây dựng, đó là những gì tôi đang hướng tới. Bất kỳ gợi ý về cách thức này có thể được thực hiện trong Scala?

Chú ý: tôi đã kết thúc việc tìm kiếm một nhiều hơn "Scalesque" cách:

class Person(name:String) { 
    val firstName::lastName::_ = name.split(" ").toList 
} 

nhưng tôi vẫn muốn nhận được một câu trả lời cho câu hỏi của tôi.

+0

Ví dụ về việc sử dụng các biến tạm thời trong quá trình khởi tạo đối tượng trên [blog scala hàng ngày] (http://daily-scala.blogspot.hu/2010/02/temporary-variables -during-object.html). –

+0

có thể trùng lặp của [Làm thế nào để bạn xác định một var địa phương/val trong constructor chính trong Scala?] (Http://stackoverflow.com/questions/1118669/how-do-you-define-a-local-var-val -in-the-primary-constructor-in-scala) –

Trả lời

10

Có cách để tránh private val. Chỉ cần sử dụng vắt của Array:

class Person(name: String) { 
    val Array(first, last) = name.split(" ") 
} 

chỉnh sửa:

gì bạn muốn làm có thể đạt được thông qua một phương pháp nhà máy trên người bạn đồng hành và một constructor mặc định mà mất đầu tiên và cuối cùng là param:

class Person(val first: String, val last: String) 

object Person { 
    def apply(name: String) = { 
    val splitted = name.split(" ") 
    new Person(splitted(0), splitted(1)) 
    } 
} 

scala> Person("Foo Bar") 
res6: Person = [email protected] 

scala> res6.first 
res7: String = Foo 

scala> res6.last 
res8: String = Bar 

Nhưng đối với trường hợp đơn giản này, tôi muốn đề xuất đầu tiên của tôi.

Ví dụ trong liên kết của bạn cũng sẽ hoạt động, nhưng nó giống như ví dụ đầu tiên của tôi. Afaik không có cách nào để tạo một biến tạm thời trong hàm tạo.

+0

Đó là một giải pháp thực sự gọn gàng, nhưng tôi muốn biết nếu có một cách 'cú pháp' định nghĩa một biến để nó chỉ tồn tại trong cấu trúc chính. Tôi cũng đã kiểm tra [link] (http://stackoverflow.com/questions/1218872/avoiding-scala-memory-leaks-scala-constructors) và họ cũng dường như làm việc đó xung quanh bằng cách nào đó. Có lẽ tôi chỉ cần làm quen với suy nghĩ chức năng :) – Chirlo

+0

cập nhật câu trả lời của tôi – drexin

3

Chỉ cần thêm vào câu trả lời @drexin. Trong ví dụ của bạn class Person(name:String) tham số nhà xây dựng vẫn được lưu trữ như private[this] val name: String và có thể được truy cập trong lớp, ví dụ:

class Person(name:String) { 
    def tellMeYourName = name 
} 

Nếu bạn thực sự muốn tránh điều này, bạn có thể tạo một đối tượng đồng hành và làm cho các nhà xây dựng chính cá nhân :

class Person private (val fName: String, val lName: String) 

object Person { 
    def apply(name: String) = { 
    val Array(fName, lName) = name split " " 
    new Person(fName, lName) 
    } 
} 

một cách khác là tạo ra một đặc điểm Person với một đối tượng đồng:

trait Person { 
    val lName: String 
    val fName: String 
} 
object Person { 
    def apply(name: String) = new Person { 
    val Array(lName, fName) = name split " " 
    } 
} 
+1

điểm tốt, tôi thực sự kết thúc với bốn lĩnh vực: name, nameParts, First Name và lastName. Tôi không thực sự hiểu tại sao Scala làm điều này, nếu tôi có một bộ sưu tập với 2000 người, mỗi người lưu trữ hai tài liệu tham khảo không mong muốn, đó là một sự lãng phí bộ nhớ. – Chirlo

+0

Trên thực tế, tên sẽ vẫn là một [private] val này chỉ khi nó được truy cập ở bất kỳ đâu bên ngoài constructor. Nếu không, nó sẽ bị xóa. Bạn có thể kiểm tra điều này bằng cách sử dụng javap -private Person.class –

5

Điều tôi lưu ý chỉ đơn giản là

class Person(n: String) { 
    val firstName = n.split(" ")(0) 
    val lastName = n.split(" ")(1) 
} 

Nếu bạn muốn tính toán mã phổ biến, thì câu trả lời của drexin với mảng rất đẹp. Nhưng nó đòi hỏi tri thức mà người đọc sẽ không có trong chương 5.Tuy nhiên, var với các bộ được đề cập trong chương 4, vì vậy sau đây là trong tầm tay:

class Person(n: String) { 
    val (firstName, lastName) = { val ns = n.split(" "); (ns(0), ns(1)) } 
} 
+1

Thực ra điều này sẽ tạo ra một scala.Tuple2 riêng x $ 1; hoặc thành viên tương tự. Hãy thử làm javap -private Person.class –

1

Một cách tiếp cận mà tránh vắt xác định hoặc nhu cầu về một phương pháp đối tượng bạn đồng hành là

class Person(name: String) {  
    val (firstName, lastName) = { 
    val nameParts = name.split(" ") 
    (nameParts(0), nameParts(1)) 
    } 
} 
object Example { 
    println(new Person("John Doe").firstName) 
} 

Một biểu scala kèm theo trong {..} có giá trị của biểu thức phụ cuối cùng trong phạm vi

+1

Thực ra điều này sẽ tạo ra một scala.Tuple2 riêng x $ 1; hoặc thành viên tương tự. Hãy thử làm javap -private Person.class –

+0

Đúng như vậy. Tôi đã học được điều gì đó: 'lớp công khai testdata.Person { scala cuối cùng riêng tư.Tuple2 x $ 1; java.lang cuối cùng riêng tư.String firstName; cuối cùng là java.lang.String lastName; ... ' –

1

Cách phổ biến nhất để thực hiện việc này là sử dụng đối tượng đồng hành (như được mô tả trong câu trả lời trước). Tuy nhiên, có trường hợp đây có thể là một vấn đề (ví dụ: nếu chúng ta kế thừa lớp và muốn gọi hàm tạo liên quan mà không biết logic nội bộ hoặc nếu chúng ta muốn khởi tạo lớp thông qua phản ánh).

Cách duy nhất tôi biết nếu làm việc đó trực tiếp bên trong lớp là một chút phức tạp:

class Person(name: String, x: Seq[String]) { 
    val firstName = x(0) 
    val lastName = x(1) 

    def this(name: String) { 
    this(name, name.split(" ")) 
    } 
} 

Ý tưởng là x (giống như tên) không có var hoặc val và do đó được chuyển đổi thành tin [this] val.

Trình tối ưu hóa biết rằng vì chúng không được sử dụng bên ngoài hàm tạo nên nó có thể sử dụng chúng làm tham số hàm bình thường và chúng không được lưu. Cuộc gọi nội bộ là những gì làm cho điều này có thể xảy ra.

Kết quả javap -private Person.class:

public class com.rsa.nw_spark.Person { 
    private final java.lang.String firstName; 
    private final java.lang.String lastName; 
    public java.lang.String firstName(); 
    public java.lang.String lastName(); 
    public com.rsa.nw_spark.Person(java.lang.String, scala.collection.Seq<java.lang.String>); 
    public com.rsa.nw_spark.Person(java.lang.String); 
} 

Các giải pháp đề cập nhiều lần mà trông như thế này:

class Person(name: String) {  
    val (firstName, lastName) = { 
    val nameParts = name.split(" ") 
    (nameParts(0), nameParts(1)) 
    } 
} 

không thực sự làm việc. Nó tạo ra một tuple tạm thời được lưu lại. Kết quả của javap -private Person.class:

public class com.rsa.nw_spark.Person { 
    private final scala.Tuple2 x$1; 
    private final java.lang.String firstName; 
    private final java.lang.String lastName; 
    public java.lang.String firstName(); 
    public java.lang.String lastName(); 
    public com.rsa.nw_spark.Person(java.lang.String); 
} 
Các vấn đề liên quan