2009-10-19 3 views
13

Tôi đang học Scala và có một thứ mà tôi không thể tìm hiểu về ngôn ngữ:Có loại "SELF" trong scala đại diện cho loại hiện tại không?

Một thời gian trước tôi đã lập trình rất thoải mái trong Lisaac, và trong Lisaac tôi có thể viết một lớp PERSONlist:ARRAY[SELF], tương đương với list:ARRAY[PERSON], vì SELF là loại đối tượng trong đó vị trí đó là.

Nhưng bằng cách sử dụng SELF, nếu tôi viết một lớp thứ hai STUDENT được thừa kế từ PERSON, sau đó STUDENT sẽ kế thừa mà khe thay đổi SELF cho STUDENT, vì vậy STUDENT sẽ có một danh sách STUDENT thay vì PERSON.

Điều đó có thể thực hiện được ở Scala không? Tôi không thể tìm ra bất cứ điều gì về điều đó.

Cảm ơn!

Trả lời

8

Tôi không chắc chắn nếu điều này sẽ thực sự có ích cho bạn, nhưng điều gần nhất tôi có thể nghĩ đến là this.type. Ví dụ:

scala> class A { val l: List[this.type] = Nil } 
defined class A 

scala> new A().l 
res3: List[A] = List() 

scala> class B extends A 
defined class B 

scala> new B().l 
res4: List[B] = List() 
+9

Tôi không nghĩ rằng "this.type" là những gì được gọi là ở đây mặc dù. "this.type" là một loại singleton; nó là loại cá thể "này" chỉ. Hãy thử như bạn có thể, phần tử hợp lệ "Danh sách [this.type]" có thể chứa là "this". –

+0

Có, bạn đã đúng. Tôi đã cố nhớ chính xác nó hoạt động như thế nào. – Lachlan

2

Từ khóa this trong Scala tương đương hoặc ít hơn.

Khi phát triển phần mềm có thể mở rộng nó là đôi khi thuận tiện để khai báo kiểu giá trị this một cách rõ ràng:

Rõ ràng Typed Tự Tài liệu tham khảo trong Scala
http://www.scala-lang.org/node/124

+0

Lệnh 'this' từ khóa không biểu thị một kiểu. – Blaisorblade

+0

Không ai nói điều đó. –

+0

> Từ khóa này trong Scala tương đương nhiều hoặc ít hơn. Bạn đã đề cập đến điều gì? Nếu nó không tương đương với SELF của Lisaac (đó là một loại), thì ý của bạn là gì? OP yêu cầu một cách để biểu thị một loại, vì anh ta muốn viết một cái gì đó như 'Array [Self]'. Và có một mô hình thường được sử dụng để đạt được điều đó, ngay cả khi nó có những hạn chế (xem câu trả lời của tôi). – Blaisorblade

2

Loại đơn lẻ và ETSR không giải quyết được sự cố. Bản thân tôi cũng đang tìm kiếm một tính năng tương tự ở Scala, nhưng dường như nó thiếu các chú thích tự gõ.

Có những trường hợp chú thích tự nhập như vậy có thể rất hữu ích. Hãy xem xét một ví dụ (được điều chỉnh từ Circular type parameters question example):

// we want a container that can store elements 
trait Container[E <: Element[E]] { 
    def elements: Seq[E] 
    def add(elem: E): Unit 
} 

// we want elements be aware of their enclosing container 
trait Element[E <: Element[E]] { 
    def container: Container[E] 
} 

Giả sử bạn đưa nó vào thư viện. Người tiêu dùng thư viện nên làm như sau:

object PersonContainer extends Container[Person] { 
    // actual implementation is not important 
    def elements = Nil 
    def add(p: Person) = {} 
} 

class Person extends Element[Person] {    // {1} 
    def container = PersonContainer 
} 

Đó là tất cả và mọi thứ hoạt động như mong đợi. Điều duy nhất quan tâm là người tiêu dùng thư viện là được cho là để sử dụng thông số loại tự ràng buộc (# 1 trong mã). Nhưng đó không phải là tất cả. Bây giờ giả sử bạn có một số loại mô hình ActiveRecord trong đầu và bạn muốn thêm phương thức save vào Element, chỉ ủy quyền cho phương thức add của vùng chứa đó. Đáng ngạc nhiên, nó không phải là dễ dàng:

trait Element[E <: Element[E]] { 
    def container: Container[E] 
    def save() = container.add(this) // won't compile 
} 

found : Element[E] 
required: E 

trực giác, chúng ta có một vài lựa chọn ở đây:

  • làm add phương pháp chấp nhận Element[E] thay vì E;
  • truyền this đến Element[E].

Không ai trong số các tùy chọn này thỏa đáng, chỉ vì thực tế là E là không giống như Element[E] (triển khai không buộc phải sử dụng các tham số kiểu tự-bound). Cách duy nhất tôi thấy giải quyết vấn đề này là phải có đó tự loại khái niệm trong Scala (chúng ta hãy giả sử chúng ta có nó trong ngôn ngữ yêu thích của chúng tôi):

trait Container[E <: Element] { 
    def elements: Seq[E] 
    def add(elem: E): Unit 
} 

trait Element { // the type parameter would be redundant ... 
    def save() = container.add(this) // ... and this would be possible, too, ... 
    def container: Container[this] // ... if only we could do this 
} 

Nếu trình biên dịch có thể đối xử với this (hoặc có thể từ khóa khác) , khi nó được sử dụng bên trong dấu ngoặc vuông, là loại thực hiện thực tế (tức là cùng loại với kết quả của obj.getClass), thì các vấn đề sẽ biến mất.

P.S. Có thể ai đó xem xét đưa công cụ này vào danh sách yêu thích của Scala? Thật không may, tôi không biết, làm thế nào khó để thực hiện logic như vậy kể từ khi có thể có những vấn đề với erasure khét tiếng của JVM.

P.P.S. Hoặc có thể có một số cách khác mà tôi không biết?

+0

Bạn cần một tự loại chú thích trong phần tử đặc điểm: 'đặc điểm tử [E <: Element [E]] { này: E => ... } ' khi bạn đã có, bạn có thể bỏ qua loại bị ràng buộc. – Blaisorblade

14

Có một thành ngữ cho điều này và được sử dụng rộng rãi trong khung công tác thu thập (trong tất cả các lớp * Thích, ví dụ: TraversableLike). Bạn cần phải thêm tự kiểu như một tham số kiểu (như thể trong C++ với CRTP) của lớp cha:

trait Person[+Self] { 
    this: Self => //Declare that any concrete subclass must implement Self; therefore, this can be used with type Self. 
    //val list: Array[Self] //Not sure that this will work so easily, for the same reason new T[] does not work in Java. 
    val list = Seq[Self]() //No problem here; Array is really special. 
} 

Sau khi xác định lớp học này, chúng ta có thể cố gắng xác định các lớp con trong người phiên dịch:

scala> class Student extends Person[Student] 
defined class Student 
scala> (new Student).list 
res0: Seq[Student] = List() //Note the result type 
scala> class Student2 extends Person[Student] //Note the mistake 
<console>:9: error: illegal inheritance; 
self-type Student2 does not conform to Person[Student]'s selftype Person[Student] with Student 
     class Student2 extends Person[Student] 

Một sai lầm mà không ngăn cản được việc định nghĩa này, nơi tự không được định nghĩa lại:

scala> class SpecStudent extends Student 
defined class SpecStudent 

Nhờ + trước Self, mà làm cho nó một tham số kiểu hiệp biến (Tôi không giải thích đó là những gì), điều này tuy nhiên là ít nhất có thể:

scala> class SpecStudentCorrect extends Student with Person[SpecStudentCorrect] 

scala> (new SpecStudentCorrect).list 
(new SpecStudentCorrect).list 
res1: Seq[SpecStudentCorrect] = List() 
+0

Bạn có chắc chắn câu hỏi cũ hơn hai năm này không được đáp ứng đầy đủ bằng câu trả lời được chấp nhận không? –

+1

Nếu bạn đọc nhận xét cho câu trả lời đó, bạn sẽ đồng ý với tôi. – Blaisorblade

+1

Hơn nữa, tại sao bạn cho rằng tôi không kiểm tra, thậm chí không đọc câu trả lời được chấp nhận?Bây giờ, liên kết bạn cung cấp thực sự cung cấp một câu trả lời tốt hơn so với liên kết được chấp nhận, nhưng tôi đã tìm thấy văn bản của câu trả lời của bạn khó hiểu. Tuy nhiên, giải pháp tôi đưa ra là giải pháp được sử dụng phổ biến nhất, bởi vì nó tốt hơn là được mô tả trong liên kết đó: bạn không thể thêm chú thích tự nhập như 'this: Type =>' khi 'Type' là abstract loại thành viên _of cùng class_, nhưng chỉ khi nó ở trong lớp ngoài. Ví dụ: – Blaisorblade