gpt4 book ai didi

Scala 实例值范围

转载 作者:行者123 更新时间:2023-12-02 08:16:25 25 4
gpt4 key购买 nike

请注意,这个问题和类似的问题之前已经被问过,例如 Forward References - why does this code compile? ,但我发现答案仍然留下了一些悬而未决的问题,所以我将再次尝试解决这个问题。

在方法和函数中, val 的效果关键字似乎是词法的,即

def foo {
println(bar)
val bar = 42
}

产量

error: forward reference extends over definition of value bar

但是,在类中, val 的范围规则似乎发生了变化:

object Foo {
def foo = bar
println(bar)
val bar = 42
}

这不仅可以编译,还可以编译 println在构造函数中将产生 0作为其输出,同时调用 foo实例完全构建后将得到预期值 42 .

因此,方法似乎可以前向引用实例值,最终,实例值将在调用方法之前进行初始化(当然,除非您从构造函数中调用它),并且 for 语句在构造函数中以相同的方式前向引用值,在初始化之前访问它们,从而导致愚蠢的任意值。

由此产生了几个问题:

  • 为什么val在构造函数中使用其词法编译时效果?

鉴于构造函数实际上只是一个方法,这似乎与完全删除 val 相当不一致。的编译时效果,仅赋予其通常的运行时效果。

  • 为什么val ,实际上失去了声明不可变值的效果?

在不同时间访问该值可能会导致不同的结果。对我来说,这很像是编译器实现细节泄露的情况。

  • 合法的用例可能是什么样的?

我很难想出一个绝对需要 val 当前语义的示例。在构造函数中,并且不容易用适当的词法来实现 val ,可能与 lazy 结合使用.

  • 如何解决 val 的这种行为,恢复人们习惯于在其他方法中使用它的所有保证?

大概可以声明所有实例 vallazy为了回到 val无论如何访问它们,都是不可变的并且产生相同的结果,并且使在常规方法中观察到的编译时效果不太相关,但这对我来说对于这类事情来说似乎是一个非常糟糕的黑客。

鉴于这种行为在实际语言中不太可能改变,编译器插件是否是解决此问题的正确位置,或者是否可以实现 val - 类似的关键字,对于刚刚花了一个小时调试由这种奇怪现象引起的问题的人来说,语言中更明智的语义?

最佳答案

仅部分答案:

Given that a constructor is really just a method ...

事实并非如此。

  • 它不返回结果,也不声明返回类型(或没有名称)
  • 不能再次调用该类的对象,例如 "foo".new ("bar")
  • 您无法对派生类隐藏它
  • 你必须用“new”来调用它们
  • 他们的名字由类(class)名称固定

从语法上看,Ctor 看起来有点像方法,它们接受参数并有一个主体,但仅此而已。

  • 为什么 val 实际上失去了声明不可变值的效果?

事实并非如此。您必须采用一个不能为 null 的基本类型才能获得这种错觉 - 对于对象,它看起来有所不同:

object Foo {
def foo = bar
println (bar.mkString)
val bar = List(42)
}
// Exiting paste mode, now interpreting.

defined module Foo

scala> val foo=Foo
java.lang.NullPointerException

你不能改变 val 2 次,你不能给它一个与 null 或 0 不同的值,你不能把它改回来,并且不同的值只能用于基本类型。所以这远不是一个变量——它是一个——可能是未初始化的——最终值。

  • 合法的用例可能是什么样的?

我想在 REPL 中工作并提供交互式反馈。您可以在没有显式包装对象或类的情况下执行代码。要获得这种即时反馈,不能等到(隐式)对象获得其结束 }。因此,类/对象不会以两遍方式读取,其中首先执行所有声明和初始化。

  • 如何解决 val 的这种行为,恢复在其他方法中使用它所习惯的所有保证?

不要读取 Ctor 中的属性,就像您不读取 Java 中的属性一样,这可能会在子类中被覆盖。

更新

Java 中也会出现类似的问题。编译器会阻止直接访问未初始化的最终属性,但如果您通过其他方法调用它:

public class FinalCheck
{
final int foo;

public FinalCheck ()
{
// does not compile:
// variable foo might not have been initialized
// System.out.println (foo);

// Does compile -
bar ();

foo = 42;
System.out.println (foo);
}

public void bar () {
System.out.println (foo);
}
public static void main (String args[])
{
new FinalCheck ();
}
}

...您会看到 foo 的两个值。

0
42

我不想原谅这种行为,而且我同意,如果编译器能够在 Java 和 Scala 中发出警告,那就太好了。

关于Scala 实例值范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10257289/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com