编辑:提示此问题的错误 has now been fixed .
在 Scala 引用资料中,我可以读到(第 86 页):
The interpretation of an assignment to a simple variable x = e depends on the definition of x. If x denotes a mutable variable, then the assignment changes the current value of x to be the result of evaluating the expression e. The type of e is expected to conform to the type of x. If x is a parameterless function defined in some template, and the same template contains a setter function x_= as member, then the assignment x = e is interpreted as the invocation x_=(e) of that setter function. Analogously, an assignment f .x = e to a parameterless function x is interpreted as the invocation f.x_=(e).
因此,例如,这样的方法就可以正常工作:
class A {
private var _a = 0
def a = _a
def a_=(a: Int) = _a = a
}
然后我可以写
val a = new A
a.a = 10
但是如果我像这样定义类,向方法 a 添加类型参数:
class A {
private var _a = 0
def a[T] = _a
def a_=(a: Int) = _a = a
}
那么它就不再起作用了;如果我写 a.a = 10
,我会收到错误:重新分配给 val
。有趣的是,例如,它仍然可以在没有类型参数和隐式参数列表的情况下工作。
可以说,在这个例子中,类型参数不是很有用,但是在 DSL 的设计中,即使 getter 有类型参数,调用 setter 方法也会很棒(顺便说一下,添加类型参数在 setter 上是允许的并且工作正常)。
所以我有三个问题:
- 有解决方法吗?
- 当前行为是否应被视为错误?
- 为什么编译器强制使用 getter 方法来允许使用 setter 的语法糖?
更新
这就是我真正想做的事情。它相当长,抱歉,我本想避免它,但我意识到省略它会更令人困惑。
我正在 Scala 中使用 SWT 设计 GUI,并且使用 Dave Orme 的 XScalaWT 获得了巨大的乐趣。 ,这极大地减少了所需的代码量。以下是他的博客文章中的一个示例,介绍如何创建将 °C 转换为 °F 度的 SWT Composite
:
var fahrenheit: Text = null
var celsius: Text = null
composite(
_.setLayout(new GridLayout(2, true)),
label("Fahrenheit"),
label("Celsius"),
text(fahrenheit = _),
text(celsius = _),
button(
"Fahrenheit => Celsius",
{e : SelectionEvent => celcius.setText((5.0/9.0) * (fahrenheit - 32)) }
),
button(
"Celsius -> Fahrenheit",
{e : SelectionEvent => fahrenheit.setText((9.0/5.0) * celsius + 32) })
)
)
每个小部件构造方法的参数都是 (WidgetType => Any)*
类型,带有一些有用的隐式转换,例如允许直接为小部件指定一个字符串有一个 setText()
方法。所有构造函数都是从单例对象导入的。
最后,我希望能够写出这样的东西:
val fieldEditable = new WritableValue // observable value
composite(
textField(
editable <=> fieldEditable,
editable = false
),
checkbox(
caption = "Editable",
selection <=> fieldEditable
)
)
这将通过 WritableValue 变量将文本字段的可编辑属性绑定(bind)到复选框的选择。
首先:命名参数在这里不适用,因此 editable = false
行必须来自某个地方。因此,沿着单例对象中的小部件构造方法,我可以从概念上编写:
def editable_=[T <: HasEditable](value: Boolean) = (subject: T) => subject.setEditable(value)
...但这仅在 getter 也存在时才有效。太棒了:无论如何我都需要 getter 来实现与 <=> 的数据绑定(bind)。像这样的事情:
def editable[T <: HasEditable] = new BindingMaker((widget: T) => SWTObservables.observeEditable(widget))
如果这有效,生活会很美好,因为我可以在 BindingMaker 中定义 <=> 并且可以使用这个很好的语法。但可惜的是,getter 上的类型参数破坏了 setter。因此,我最初的问题是:为什么这个简单的类型参数会影响编译器是否决定继续使用语法糖来调用 setter?
我希望现在可以更清楚一些了。感谢您的阅读...
最佳答案
更新根据新信息删除了之前的整个答案。
这里发生了很多非常奇怪的事情,所以我将尝试解释我对您目前所掌握的内容的理解:
def editable_=[T <: HasEditable](value: Boolean) = (subject: T) => subject.setEditable(value)
这是一个 setter 方法,并且纯粹存在,因此它可以看起来像是一个命名参数 在你的 DSL 中。它什么也不设置,实际上返回一个函数。
textField(
editable <=> fieldEditable,
editable = false
)
这是调用 textField
工厂方法,其看起来是一个命名参数,但实际上是之前定义的 setter 方法。
令人惊讶的是,尽管我最初担心编译器会将其识别为命名参数并产生语法错误,但该方法似乎有效。我使用简单的单态(非泛型)方法对其进行了测试,尽管它确实需要定义 getter 方法才能将 setter 视为如此 - 您已经注意到这一事实。
编写 DSL 时通常需要一定程度的“聪明才智”(否则会被完全禁止),因此您的初衷不清楚也就不足为奇了。这也许是 Scala 中从未见过的全新技术。 setter 和 getter 定义的规则基于将它们用作 getter 和 setter,因此,当您像这样突破边界时,如果事情有点破裂,请不要感到惊讶。
看来这里真正的问题是您使用类型参数的方式。在这个表达式中:
def editable_=[T <: HasEditable](value: Boolean) = (subject: T) => subject.setEditable(value)
编译器无法从提供的参数中推断出特定的T
,因此它将采用允许的最通用类型(在本例中为HasEditable
)。您可以通过在使用该方法时显式提供类型参数来更改此行为,但这似乎会破坏您想要实现的目标。
鉴于函数不能是泛型的(只有方法可以),我怀疑您根本不需要类型界限。因此,您可以尝试的一种方法是删除它们:
def editable_=(value: Boolean) = (subject: HasEditable) => subject.setEditable(value)
def editable = new BindingMaker((widget: HasEditable) => SWTObservables.observeEditable(widget))
关于scala - 关于 Scala 的赋值和 setter 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4967492/