簡體   English   中英

在按名稱調用參數期間引用類變量

[英]Referencing a class variable during call-by-name parameter

我有一個正在構建的庫,其中有一個特定的類,我想在其中使該類的一部分可定制。 用戶將能夠傳入一段代碼,以便他們可以根據需要修改內容(當然在某些范圍內)。 這實際上是為了避免讓用戶為小的差異擴展不同的類實現。

我原本以為我可以按名稱傳遞,但問題是在這種情況下我不能引用i2 (這對我來說很有意義)。

class MyClass[T](i: Int)(body: => T) {
  var i2 = i
  val something = body
}

val myc = new MyClass(1)(
  //Do some work on i2 here
  println(s"i2") //Compile error, i2 not found
)

有沒有辦法做這樣的事情,我可以在自定義body定義期間引用內部類變量/方法? 我一直在尋找,我相信我在這里“不確定要問什么問題”領域。

繼承有什么問題?

class MyClass[T](s: T){
    var i2 = s
}

val myc = new MyClass("You should not use mutable vars") { 
    i2 += "Because this is not how scala is intended to be used!"
    i2 += "Providing library methods allowing users mutate internal structures"
    i2 += " is especially bad!"
    i2 += " You really, really, really, really should NOT be doing this!"

    println(s"$i2") 
}

或者,您可以將類實例作為參數傳遞給函數:

    class MyClass[T](s: T)(body: MyClass[T] => Unit) { 
       var i2 = s
       val something = body(this)
    }

    val myC = new MyClass("This approach") { x => 
        x.i2 += " isn't really any better then the other one."
        x.i2 += " If the user wants a different value for i2, "
        x.i2 += " then why don't they pass it in to begin with?"
        x.i2 += " what is the point in passing one value"
        x.i2 += " and a function that replaces it with another?"
        x.i2 += " Mutable vars are evil."
        x.i2 += " Do not use them!"
        println(x.i2)
    }
        

您可以嘗試將 by-name 參數MyClass[T] => T的函數,然后使用body(this)調用它。 如果圍繞類中的初始化順序執行此操作,請小心。 我忘記了它是否能夠訪問MyClass的非公共成員,盡管在使用它與定義的擴展點進行交互時這可能不是問題。

class MyClass[T](i: Int)(body: MyClass[T] => T) {
  var i2 = i

  val something = body(this)
}

類型推斷有點瘋狂,但是:

scala> val myc = new MyClass[Unit](1)(x => println(s"${x.i2}"))
1
val myc: MyClass[Unit] = MyClass@3f875466

scala> myc.something

當然,在something例子中是Unit value () ,它被 sbt 控制台抑制。 通過讓body成為一個高階函數,可以注入行為:

scala> val otherMyc = new MyClass[Int => Unit](1)(x => inc => x.i2 += inc)
val otherMyc: MyClass[Int => Unit] = MyClass@2ec032f4

scala> otherMyc.i2
val res4: Int = 1

scala> otherMyc.something(2)

scala> otherMyc.i2
val res6: Int = 3

我突然想到,您可能能夠使用只能具有在MyClass中構造的實例的類(例如,在具有有限構造函數可見性的伴隨對象中定義的類)並將其作為第二個參數傳遞給body (即讓body a (MyClass[T], MyClass.PrivateEvidence) => T )。 然后,您可以通過讓它們需要MyClass.PrivateEvidence參數來使原本是私有的方法成為有效的私有方法(他們可以進一步檢查它是否是該實例的PrivateEvidence )。 當然,本質上沒有什么可以阻止用戶提供的body泄露PrivateEvidence

您還可以定義一個trait ,其中包含允許擴展操作的內容。 MyClass然后構造該特征的實例並將其傳遞給body ,如下所示:

trait MyClassAccess {
  def setI2(x: Int): Unit
  def i2: Int
}

class MyClass[T](i: Int)(body: MyClassAccess => T) {
  private var i2: Int = i
  private val mcthis = this
  private val access = new MyClassAccess {
    def setI2(x: Int): Unit = mcthis.i2 = x
    def i2: Int = mcthis.i2
  }

  val something = body(access)
}
scala> val adder: MyClassAccess => Int => Unit = { mca => increment => mca.setI2(mca.i2 + increment); println(s"i2 is ${mca.i2}") }
val adder: MyClassAccess => (Int => Unit) = $Lambda$5580/0x0000000840d16040@218339fd

scala> new MyClass(1)(adder)
val res9: MyClass[Int => Unit] = MyClass@c5adf11

scala> res9.something(1)
i2 is 2

scala> res9.something(2)
i2 is 4

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM