2009-08-20 22 views
30

dụ:Làm thế nào để nhanh chóng một thể hiện của loại đại diện bởi tham số gõ vào Scala

import scala.actors._ 
import Actor._ 

class BalanceActor[T <: Actor] extends Actor { 
    val workers: Int = 10 

    private lazy val actors = new Array[T](workers) 

    override def start() = { 
    for (i <- 0 to (workers - 1)) { 
     // error below: classtype required but T found 
     actors(i) = new T 
     actors(i).start 
    } 
    super.start() 
    } 
    // error below: method mailboxSize cannot be accessed in T 
    def workerMailboxSizes: List[Int] = (actors map (_.mailboxSize)).toList 
. 
. 
. 

Lưu ý các lỗi thứ hai cho thấy rằng nó biết các mục diễn viên là "T" s, nhưng không phải là chữ "T" là một lớp con của tác nhân, như bị ràng buộc trong định nghĩa chung của lớp.

Mã này có thể được sửa chữa để hoạt động như thế nào (sử dụng Scala 2.8)?

+0

... quên đề cập đến, tôi đang sử dụng plugin Eclipse Scala (2,8 đêm) cho điều này ... –

+0

Vẫn gặp lỗi trên "phương thức mailboxSize không thể truy cập trong T", mặc dù sử dụng fac () chức năng được truyền vào như bạn gợi ý. Tôi ngạc nhiên bởi kết quả này, vì trình biên dịch biết rằng T là <: Diễn viên, và diễn viên đó có .mailboxSize (được truy cập trong cùng lớp BalanceActor), tôi tự hỏi nếu đây là lỗi trong phiên bản cụ thể của 2,8 đêm tôi đang sử dụng ??? Không nên truy cập vào biên dịch .mailboxSize, như bạn đã tự tuyên bố? Bạn đã có một cái gì đó tương tự như làm việc, có lẽ trên các plugin Eclipse 2.7.5.final, hoặc biên dịch scalac độc lập? –

+0

Nhờ cả oxbow_lakes và Walter Chang để cung cấp các giải pháp khác nhau, nhưng cả hai hoàn toàn khả thi, cho vấn đề instantiation. –

Trả lời

20

EDIT - xin lỗi, tôi chỉ vừa mới nhận thấy lỗi đầu tiên của bạn. Không có cách nào instantiating một T khi chạy vì loại thông tin bị mất khi chương trình của bạn được biên dịch (thông qua loại tẩy xoá)

Bạn sẽ phải vượt qua trong một số nhà máy để đạt được việc xây dựng:

class BalanceActor[T <: Actor](val fac:() => T) extends Actor { 
    val workers: Int = 10 

    private lazy val actors = new Array[T](workers) 

    override def start() = { 
    for (i <- 0 to (workers - 1)) { 
     actors(i) = fac() //use the factory method to instantiate a T 
     actors(i).start 
    } 
    super.start() 
    } 
} 

Điều này có thể được sử dụng với một số diễn viên CalcActor như sau:

val ba = new BalanceActor[CalcActor]({() => new CalcActor }) 
ba.start 

là một sang một bên: bạn có thể sử dụng until thay vì to:

val size = 10 
0 until size //is equivalent to: 
0 to (size -1) 
+0

Đã tìm ra đề xuất của bạn liên quan đến loại đặc điểm kỹ thuật, nhưng các lỗi không thay đổi như kết quả –

+0

Xin lỗi - đã thay đổi câu trả lời của tôi - Tôi chỉ thấy lỗi thứ 2 và không nhận thấy dòng mới T23 –

+0

Cảm ơn bạn đã đề xuất. Làm thế nào sự hiện diện của nhà máy giúp với lỗi gọi một phương thức cụ thể cho các lớp con của Actor (chẳng hạn như mailboxSize được hiển thị trong ví dụ)? Cảm ơn bạn đã nhắc nhở về "cho đến", nhưng rất khó để phá vỡ nhiều thập kỷ của thói quen từ C và Java ... ;-) –

14

Sử dụng Manifest:

class Foo[A](a: A)(implicit m: scala.reflect.Manifest[A]) { 
    def create: A = m.erasure.newInstance.asInstanceOf[A] 
} 

class Bar 

var bar1 = new Bar  // prints "bar1: Bar = [email protected]" in console 
val foo = new Foo[Bar](bar1) 
val bar2 = foo.create // prints "bar2: Bar = [email protected]" in console 
bar2.isInstanceOf[Bar] // prints "Boolean = true" in console 

BTW, Manifest được cung cấp tài liệu trong 2.7.X nên sử dụng nó một cách cẩn thận. Cùng một mã hoạt động trong 2.8.0 hàng đêm là tốt.

+0

Và không hoạt động nếu các tham số hàm tạo của A của bạn là –

+0

Cách của bạn hoạt động, cũng như oxbow_lakes ', để giải quyết vấn đề tạo các phiên bản mới của A (T là ví dụ ban đầu) Nhưng ... lỗi "method mailboxSize không thể được truy cập trong A "còn lại. Bất kỳ ý tưởng nào tại sao? –

+0

@Paul Nó cũng là câu đố của tôi. "self.mailboxSize" thực hiện thành công trong 2.7.5 REPL nhưng tăng "phương thức mailboxSize không thể được truy cập trong scala.actors.Actor" lỗi trong 2.8.0. "def workerMailboxSizes" biên dịch cũng tốt trong 2.7.5. –

2

Bạn không thể, như đã đề cập, tạo nhanh T do xóa. Vào thời gian chạy, không có T. Điều này không giống như các mẫu của C++, trong đó sự thay thế xảy ra là thời gian biên dịch và nhiều lớp thực sự được biên dịch, cho mỗi biến thể trong việc sử dụng thực tế.

Giải pháp tệp kê khai thú vị, nhưng giả định có một hàm tạo cho T không yêu cầu tham số. Bạn không thể giả định điều đó.

Đối với vấn đề thứ hai, phương pháp mailboxSize được bảo vệ, vì vậy bạn không thể gọi nó trên một đối tượng khác. Cập nhật: điều này chỉ đúng với Scala 2.8.

11

Hiện tại, đây là cách thích hợp và an toàn hơn để thực hiện việc này. Scala 2.10 đã giới thiệu TypeTags, cho phép chúng ta khắc phục vấn đề xóa khi sử dụng các kiểu generic.

tại Có thể parameterise lớp học của bạn như sau:

class BalanceActor[T <: Actor :ClassTag](fac:() => T) extends Actor { 
    val actors = Array.fill[T](10)(fac()) 
} 

Bằng cách này, chúng ta đang đòi hỏi một ClassTag ngầm [T] có sẵn khi lớp được khởi tạo. Trình biên dịch sẽ đảm bảo đây là trường hợp và sẽ tạo mã mà chuyển ClassTag [T] vào trong hàm tạo lớp. ClassTag [T] sẽ chứa tất cả các thông tin về T, và kết quả của thông tin này có sẵn cho trình biên dịch tại thời gian biên dịch (pre-erasure) bây giờ cũng có sẵn trong thời gian chạy, cho phép chúng ta xây dựng một mảng [T].

Lưu ý rằng nó vẫn không thể làm:

class BalanceActor[T <: Actor :ClassTag] extends Actor { 
    val actors = Array.fill[T](10)(new T()) 
} 

Lý do này không làm việc là trình biên dịch không có cách nào để biết liệu lớp T có một constructor không có arg.

+0

Lợi thế của việc này qua câu trả lời được chấp nhận là gì? Ví dụ mã của bạn không làm rõ điều đó. – 2rs2ts

+0

Xin lỗi, ví dụ của tôi không tuyệt vời - tôi đã cải thiện nó ngay bây giờ. Câu trả lời được chấp nhận thực sự không biên dịch. 'mảng mới [T] (công nhân)' không hoạt động vì kiểu T không được biết khi chạy. Câu trả lời thứ hai với Manifest là tốt hơn, nhưng TypeTags đã thay thế Manifest. – Josh

+0

BTW, lý do giải pháp này là tốt hơn so với Manifest là sử dụng Manifest là không an toàn - nó giả định rằng có một constructor no-arg cho T, mà có thể không phải là trường hợp. – Josh

Các vấn đề liên quan