2012-04-15 26 views
5

Tôi đã cố gắng nắm bắt các tham số ngầm định hoạt động như thế nào trong Scala. Theo như tôi có thể nói, độ phân giải tham số ngầm sẽ diễn ra như sau:Vals lười biếng và các thông số tiềm ẩn trong Scala

  1. Truyền đạt đối tượng một cách rõ ràng.
  2. xác định ngầm định được xác định trong phạm vi.
  3. đối tượng Companion của lớp được sử dụng như một tham số ngầm

Tuy nhiên, khi tôi bắt đầu chơi xung quanh với điều này kết hợp Vals lười biếng tôi có một chút của một supprise. Có vẻ như những người lười biếng lười biếng chỉ sử dụng quy tắc giải quyết cuối cùng. Dưới đây là một số mẫu mã để minh họa:

class Bar(val name:String) 
object Bar { implicit def bar = new Bar("some default bar") } 

class Foo { 
    lazy val list = initialize 
    def initialize(implicit f:Bar) = { 
    println("initialize called with Bar: '" + f.name + "' ...") 
    List[Int]() 
    } 
} 

trait NonDefaultBar extends Foo { 
    implicit def f = new Bar("mixed in implicit bar") 
    def mixedInInit = initialize 
    lazy val mixedInList = list 
} 

object Test { 
    def test = { 
     println("Case 1: with implicitp parameter from companion object") 
     val foo1 = new Foo 
     foo1.list 
     foo1.initialize 

     println("Case 2: with mixedin implicit parameter overriding the default one...") 
     val foo2 = new Foo with NonDefaultBar 
     foo2.mixedInList 

     val foo3 = new Foo with NonDefaultBar 
     foo3.mixedInInit 

     println("Case 3: with local implicit parameter overriding the default one...") 
     implicit def nonDefaultBar = new Bar("locally scoped implicit bar") 
     val foo4 = new Foo 
     foo4.list 
     foo4.initialize 
    } 
} 

Calling Test.test cho đầu ra sau đây:

Case 1: with implicitp parameter from companion object 
initialize called with Bar: 'some default bar' ... 
initialize called with Bar: 'some default bar' ... 
Case 2: with mixedin implicit parameter overriding the default one... 
initialize called with Bar: 'some default bar' ... 
initialize called with Bar: 'mixed in implicit bar'... 
Case 3: with local implicit parameter overriding the default one... 
initialize called with Bar: 'some default bar' ... 
initialize called with Bar: 'locally scoped implicit bar' ... 

Tại sao trình biên dịch không bắt rằng có một Bar implict trộn lẫn trong khi gọi mixedInList trong trường hợp 2. Trong trường hợp 3 nó cũng bỏ qua thanh địa chỉ được xác định cục bộ khi truy cập vào danh sách.

Có cách nào để sử dụng các tham số ngầm với vals lười không sử dụng ngầm định được xác định trong đối tượng đồng hành không?

Trả lời

4

Đó là vì không có khác implicit Bar, khi trình biên dịch biên dịch lớp Foo. Mã decompiled trong Java giống như thế này:

public class Foo 
    implements ScalaObject 
{ 
    private List<Object> list; 
    public volatile int bitmap$0; 

    public List<Object> list() 
    { 
    if (
     (this.bitmap$0 & 0x1) == 0); 
    synchronized (this) 
    { 
     if (
     (this.bitmap$0 & 0x1) == 0) { 
     this.list = initialize(Bar..MODULE$.bar()); this.bitmap$0 |= 1; } return this.list; 
    } 
    } 
    public List<Object> initialize(Bar f) { Predef..MODULE$.println(new StringBuilder().append("initialize called with Bar: '").append(f.name()).append("' ...").toString()); 
    return Nil..MODULE$; 
    } 
} 

các lazy val chỉ là một phương pháp để kiểm tra nếu biến đã được thiết lập và một trong hai trả về nó, hoặc đặt nó và sau đó trả về nó. Vì vậy, mixin của bạn không được tính đến chút nào. Nếu bạn muốn điều đó, bạn phải tự lo việc khởi tạo.

+0

Ok, nhưng * không * nó nhận ra sự liên quan? Họ được biết đến tại compiletime phải không? Có lẽ nó sẽ làm việc nếu tôi di chuyển các tham số tiềm ẩn để các nhà xây dựng? –

+0

Nó nhận ra Thanh chỉ tiềm ẩn nằm trong phạm vi thời gian biên dịch, và đó là một trong thanh đồng hành. Bởi vì 'list' chỉ được định nghĩa trên' Foo', nó sẽ luôn luôn sử dụng cái này. Bạn có thể định nghĩa ngầm định trong hàm tạo của bạn, nhưng ngầm định trong đặc điểm sẽ vẫn không nằm trong phạm vi, vì nó chỉ tồn tại trên cá thể. – drexin

+0

Vì vậy, nếu tôi di chuyển phương thức val và khởi tạo lazy sang đặc điểm 'NonDefaultBar' và để cho đặc điểm đó mở rộng' Foo' và cũng di chuyển def deficit sang 'Foo', thì trình biên dịch sẽ biết lấy def deficit trong' Foo 'khi đánh giá 'danh sách val lười biếng'? –

0

Mặc dù scala không hỗ trợ các thông số ẩn với các tham số ngầm, bạn có thể tự xác định rằng mình sử dụng các tùy chọn. Vì vậy, một giải pháp là thay thế:

lazy val list = initialize 

bởi

private var _list: Option[List[Int]] = None 
def list(implicit f: Bar) = 
    _list.getOrElse{ 
    _list = Some(initialize) 
    _list.get 
    } 

Sau đó chạy Test.test hiển thị các kết quả mong đợi:

Case 1: with implicitp parameter from companion object 
initialize called with Bar: 'some default bar' ... 
initialize called with Bar: 'some default bar' ... 
Case 2: with mixedin implicit parameter overriding the default one... 
initialize called with Bar: 'mixed in implicit bar' ... 
initialize called with Bar: 'mixed in implicit bar' ... 
Case 3: with local implicit parameter overriding the default one... 
initialize called with Bar: 'locally scoped implicit bar' ... 
initialize called with Bar: 'locally scoped implicit bar' ... 

Lưu ý rằng nếu bạn có mutable options, bạn có thể thay thế lười biếng của bạn val chỉ với hai dòng để có được kết quả tương tự.

private val _list: MutableOpt[List[Int]] = MutableOpt.from(None) 
def list(implicit f: Bar) = _list.getOrSet(initialize) 

Cá nhân, tôi hy vọng một ngày nào đó Scala sẽ cho phép chúng tôi viết những dòng này sử dụng một dòng:

lazy val list(implicit f: Bar) = initialize 

đó sẽ là một cách hoàn hảo ý nghĩa: Bất cứ lúc nào bạn muốn truy cập giá trị của danh sách biến, bạn cần một thanh trong phạm vi, mặc dù chỉ tính toán đầu tiên sẽ là vấn đề.

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