考虑这样的代码(这只是示例而不是真正的代码):
class Foo(url : String) extends Bar(url)
{
def say() { println(url) }
}
它编译并且有效。无意义的结果“当然”。我太新手无法判断,但对我而言,它没有任何意义,只有混乱 - 根据定义,构造函数的参数不可能直接在另一种方法中获得。
有人在Scala中获得更多经验可以指出它可能有效还是有意义的条件?或者确认我怀疑这是当前Scala编译器中的缺陷。
更新
class Bar(val Url : String)
{
}
为什么“胡说八道”。因为我的遗嘱(在Foo中没有“var”和“val”)只是传递参数,没有别的。因此,当我实际使用构造函数参数时,只需要使用错误的实体。当你故意写作时,你每次都会获得累积奖金,但如果你没有(即拼写错误),你可以随机使用实体。这不是代码没有意义,它是计算只是没有意义,我也可以掷骰子。有一个方法范围,我没有看到为什么不应该有构造函数范围的原因。
所以看起来这个邪恶的结构实际上是语言的一部分(按设计)。正如UserUnknown和Tomasz Nurkiewicz所说,反对制造愚蠢错字的唯一防线就是惯例,但是低于大写的情况并不好。 “A”和“a”差别很大,但“U”和“u”并不多。引入一个特殊字符(如Tomasz所示)要好得多,因为可以直观地检测构造函数参数的可疑用法。
我将使用“$”表示“只是传递”构造函数参数,为我输入更难,而且你在代码中也没有经常看到这个字符。
感谢您的回答!
为什么它是邪恶的?因为用户应该明确允许隐式操作 - 好的例子在C#中是“动态的”,在Scala中是隐式转换。打破这个导致大量问题的规则的例子是 - 在C ++中隐式转换为bool,在C ++中隐式构造函数,在Perl中使用声明。而这个特殊情况非常非常接近所提到的perlism,在Perl中,最终解释器发生了变化以检测这种错误,为什么Scala重复了同样的错误?我不知道。
答案 0 :(得分:12)
你的怀疑完全没有价值。这是设计的。
类的参数是类的一部分。如有必要,它们将作为字段保存(例如在您的示例中),如果它们从未在构造外使用,则不会保留。
所以,基本上,如果你不需要它作为一个领域,它就不会。如果你这样做,它会。而且你永远不会写一个额外的代码字符来告诉编译器它可以自己解决什么。
答案 1 :(得分:7)
这不是一个错误,它是一个功能。事实上,一个非常好的。需要一个例子它有多大用处?以下是我如何通过构造函数使用Spring和依赖注入:
@Service
class Foo @Autowired() (bar: Bar, jdbcOperations: JdbcOperations) {
def serverTime() = bar.format(jdbcOperations.queryForObject("SELECT now()", classOf[Date]))
}
Java中的等效代码:
@Service
public class Foo
{
private final Bar bar;
private final JdbcOperations jdbcOperations;
@Autowired
public Foo(Bar bar, JdbcOperations jdbcOperations)
{
this.bar = bar;
this.jdbcOperations = jdbcOperations;
}
public String serverTime()
{
return this.bar.format(this.jdbcOperations.queryForObject("SELECT now()", Date.class));
}
}
还是不相信?
简短教程:
class Foo(var x: Int, val y: Int, z: Int) {
println(z)
//def zz = z
}
x
将成为var
getters 和 setter 。 y
将成为不可变变量,z
只有在取消注释zz
方法时才会成为不可变变量。否则它将仍然是构造函数参数。整齐!
更新:我现在明白你的意思了!通过访问基类中的url
变量,以下代码可以正常工作:
class Bar(val url)
class Foo(_url : String) extends Bar(_url)
{
def say() { println(url) }
}
我同意,这既丑陋又要求麻烦。事实上,当我使用Scala类作为Hibernate实体时,我曾经遇到过这个问题 - 我在基类中使用了构造函数参数而不是字段,从而导致创建重复字段:一个在基类中,一个在派生类中。我甚至都没注意到,但是Hibernate在运行时尖叫着已经定义了重复的列映射。
所以我必须在某种程度上同意你的观点 - 这有点令人困惑,可能容易出错。这是您为“隐含”和简洁代码支付的价格。
但是请注意,构造函数参数之前没有修改过的val
修饰符。在没有修改不可变字段的情况下创建,而val
另外添加了getter。
答案 2 :(得分:1)
当类中的方法引用此类参数时,Scala会从构造函数参数创建一个字段。我无法通过这种方式找到错误。
对于简单的情况,一切都按预期工作:
scala> class Bar(val url: String)
defined class Bar
scala> class Foo(url: String) extends Bar(url) {
| def say() { println(url) }
| }
defined class Foo
scala> new Foo("urlvalue").say
urlvalue
如果我们对构造函数参数的情况引入一些混淆,那么这个例子仍然按预期工作:
scala> class Bar(val Url: String)
defined class Bar
scala> class Foo(url: String) extends Bar(url) {
| def say() { println(url) }
| }
defined class Foo
scala> new Foo("urlvalue").say
urlvalue
有趣的是,除了url
中的大写Foo
之外,您可能认为这有效,因为它在Url
中引入了小写的Bar
字段,但是似乎并非如此 - 编译器似乎很聪明,知道它可以转到Url
以获得url
中say
的值,因为没有小写字段生成。
scala> :javap -private Bar
Compiled from "<console>"
public class Bar extends java.lang.Object implements scala.ScalaObject{
private final java.lang.String Url;
public java.lang.String Url();
public Bar(java.lang.String);
}
scala> :javap -private Foo
Compiled from "<console>"
public class Foo extends Bar implements scala.ScalaObject{
public void say();
public Foo(java.lang.String);
}
我唯一能看到这会让人感到困惑的是你错误拼写 var 字段。在这种情况下,您实际上会引入一个新字段,这两个字段可能会失去一步。
scala> class Bar(var Url: String)
defined class Bar
scala> class Foo(url: String) extends Bar(url) {
| def say() { println(url) }
| }
defined class Foo
scala> val f = new Foo("urlvalue")
f: Foo = Foo@64fb7efa
scala> f.say
urlvalue
scala> f.Url = "newvalue"
f.Url: String = newvalue
scala> f.say
urlvalue
scala> :javap -private Foo
Compiled from "<console>"
public class Foo extends Bar implements scala.ScalaObject{
private final java.lang.String url;
public void say();
public Foo(java.lang.String);
}