2011-09-30 37 views
8

Tôi đang cố gắng để tìm ra cách để gọi một constructor cho một loại trừu tượng Scala:Làm cách nào tôi có thể gọi hàm khởi tạo của kiểu trừu tượng Scala?

class Journey(val length: Int) 
class PlaneJourney(length: Int) extends Journey(length) 
class BoatJourney(length: Int) extends Journey(length) 

class Port[J <: Journey] { 
    def startJourney: J = { 
    new J(23) // error: class type required but J found 
    } 
} 

Đây có phải là thậm chí khả thi? Tôi quen thuộc với Scala manifests nhưng tôi không rõ làm thế nào họ có thể giúp đỡ ở đây. Tương tự như vậy tôi không thể tìm ra cách để làm điều tương tự với người bạn đồng hành của đối tượng áp dụng() constructor:

object Journey { def apply() = new Journey(0) } 
object PlaneJourney { def apply() = new PlaneJourney(0) } 
object BoatJourney { def apply() = new BoatJourney(0) } 

class Port[J <: Journey] { 
    def startJourney: J = { 
    J() // error: not found: value J 
    } 
} 

Bất kỳ suy nghĩ gratefully nhận được!

Trả lời

7

Không có cách nào trực tiếp để gọi hàm tạo hoặc truy cập đối tượng đồng hành chỉ được cung cấp một loại. Một giải pháp sẽ là sử dụng một lớp kiểu để tạo ra một cá thể mặc định của kiểu đã cho.

trait Default[A] { def default: A } 

class Journey(val length: Int) 
object Journey { 
    // Provide the implicit in the companion 
    implicit def default: Default[Journey] = new Default[Journey] { 
    def default = new Journey(0) 
    } 
} 

class Port[J <: Journey : Default] { 
    // use the Default[J] instance to create the instance 
    def startJourney: J = implicitly[Default[J]].default 
} 

Bạn sẽ cần thêm định nghĩa ngụ ý Default vào tất cả các đối tượng đồng hành của lớp hỗ trợ tạo một thể hiện mặc định.

+0

Cảm ơn Moritz - nhưng dán mã của bạn vào REPL sẽ ném một vài lỗi? Ngoài ra làm thế nào tôi sẽ thêm các tham số để mặc định "constructor"? –

+0

Bạn phải nhập chế độ dán cho mã này để làm việc trong REPL (chỉ cần gõ ': dán' trước khi dán). Cũng có một lỗi trong đoạn mã đã được Philippe sửa. – Moritz

+1

Nếu bạn muốn thêm các tham số, bạn có thể chỉ cần thêm một phương thức mới trong đặc tính 'Default'. 'ngầm định [Default [J]]' sẽ cung cấp cho bạn một cá thể của đặc điểm với các tham số kiểu được cung cấp và bạn có thể gọi bất kỳ phương thức nào bạn thích trên nó, ví dụ: 'ngầm định [Mặc định [J]]. tạo (23)'. Xem ví dụ [câu hỏi này] (http://stackoverflow.com/questions/5598085/where-does-scala-look-for-implicits) để biết chi tiết về cách hoạt động của implicits. – Moritz

0

Độ nghiêng của tôi là điều này không thể thực hiện được. Tôi xa một guru Scala, nhưng lập luận của tôi là thế này:

  1. Bạn có một Cảng lớp với T kiểu lập luận đó T phải kế thừa từ Journey (nhưng T không phải là chính xác Journey, đây là quan trọng).
  2. Trong Port, bạn xác định phương thức tạo mới T. Lớp này không biết T là gì và do đó hàm tạo của T trông như thế nào.
  3. Bởi vì bạn không biết những gì các đối số của T constructor mất, bạn không biết những gì các đối số để vượt qua nó.

Các giải pháp cho vấn đề này được xử lý rất độc đáo trong một câu hỏi khác, vì vậy tôi sẽ chỉ cho bạn có cho họ chứ không phải lặp đi lặp lại ở đây: Abstract Types/Type Parameters in Scala

7

lớp của bạn cần một tham số constructor ngầm để có được những Manifest. Sau đó, bạn có thể gọi tẩy xoá để lấy số Class và gọi số newInstance, gọi một cách phản ánh hàm tạo null nếu có.

class J[A](implicit m:Manifest[A]) { 
    def n = m.erasure.newInstance() 
} 

new J[Object].n 

Thuộc tính Scala 2.10, thuộc tính erasure trong tệp kê khai không còn được dùng nữa. def n = m.runtimeClass.newInstance() thực hiện tương tự nhưng không có cảnh báo.

+0

Cách tiếp cận này có yêu cầu một hàm tạo tham số không? –

+0

@Chris Nó có, và đó là giới hạn. Trừ khi bạn thực hiện một đặc điểm cho bất kỳ nhà máy là cần thiết. –

+0

Cảm ơn Kim - thật thú vị khi có hàm tạo newInstance() "được giải thích rõ ràng –

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