なぜ `private val`と` private final val`が違うのですか?


100

私はそれを考えるために使用されるprivate valprivate final val、私はScalaのリファレンスセクション4.1を見るまで、同じです。

定数値の定義は次の形式です。

final val x = e

ここで、eは定数式です(§6.24)。最後の修飾子が存在する必要があり、型注釈を与えることはできません。定数値xへの参照自体は、定数式として扱われます。生成されたコードでは、定義の右側に置き換えられます。

そして私はテストを書きました:

class PrivateVal {
  private val privateVal = 0
  def testPrivateVal = privateVal
  private final val privateFinalVal = 1
  def testPrivateFinalVal = privateFinalVal
}

javap -c 出力:

Compiled from "PrivateVal.scala"
public class PrivateVal {
  public int testPrivateVal();
    Code:
       0: aload_0       
       1: invokespecial #19                 // Method privateVal:()I
       4: ireturn       

  public int testPrivateFinalVal();
    Code:
       0: iconst_1      
       1: ireturn       

  public PrivateVal();
    Code:
       0: aload_0       
       1: invokespecial #24                 // Method java/lang/Object."<init>":()V
       4: aload_0       
       5: iconst_0      
       6: putfield      #14                 // Field privateVal:I
       9: return
}

バイトコードは、Scalaリファレンスが言ったとおりprivate valです:はありませんprivate final val

なぜscalacは単にprivate valとして扱わないのprivate final valですか?根本的な理由はありますか?


28
言い換えると、a valはすでに不変なので、なぜfinalScalaでキーワードがまったく必要なのでしょうか。コンパイラがすべてvalのをと同じように処理できないのはなぜfinal valですか?
Jesper、

privateスコープ修飾子はpackage privateJava と同じセマンティクスを持っていることに注意してください。あなたは言うつもりかもしれませんprivate[this]
Connor Doyle、

5
@ConnorDoyle:パッケージプライベートとして?私はそうは思わない:private平均それは、このクラスのインスタンスにのみ表示されていることprivate[this]だけで、このインスタンス-同じのインスタンスを除き、クラスprivate誰もが(同じパッケージから含める)値にアクセスすることはできません。
Make42 2016年

回答:


81

したがって、これは推測にすぎませんが、右側にリテラルがある最後の静的変数が定数としてバイトコードにインライン化されるのは、Javaで根強い不快感でした。これは確かにパフォーマンス上の利点をもたらしますが、「定数」が変更されると、定義のバイナリ互換性が失われます。値を変更する必要がある可能性がある最終的な静的変数を定義する場合、Javaプログラマーは、メソッドまたはコンストラクターで値を初期化するなどのハックに頼らなければなりません。

Scalaのvalは、Javaの意味ですでに最終版です。Scalaの設計者が「定数値をインライン化する許可」を意味するために冗長修飾子finalを使用しているようです。したがって、Scalaプログラマーは、ハックに頼らずにこの動作を完全に制御できます。インライン化された定数、つまり、決して変更されないが高速な値が必要な場合は、「final val」と書き込みます。バイナリ互換性を損なうことなく値を変更できる柔軟性が必要な場合は、「val」のみを使用します。


9
はい、それが非プライベートvalの理由ですが、プライベートvalは他のクラスにインライン化できず、同じ方法で互換性を壊すことはできません。
Alexey Romanov

3
に変更private valした場合、バイナリ互換性の問題はありますprivate final valか?
ヤンボー

1
@ steve-waldmanすみませんval、2番目の段落でですか?
ヤンボー

1
バイナリ互換性に関するJavaの最終的な静的変数の詳細は次のとおりです-docs.oracle.com/javase/specs/jls/se7/html/…–
Eran Medan

8

ここでの混乱は、不変性をfinalのセマンティクスと混同することから生じていると思います。 valsは子クラスでオーバーライドできるため、明示的にマークされていない限り、finalとして扱うことはできません。

@Brian REPLは、行レベルでクラススコープを提供します。見る:

scala> $iw.getClass.getPackage
res0: Package = package $line3

scala> private val x = 5
<console>:5: error: value x cannot be accessed in object $iw
  lazy val $result = `x`

scala> private val x = 5; println(x);
5

1
私は話しているprivate val。オーバーライドできますか?
ヤンボー

いいえ、プライベートバリューはオーバーライドできません。サブクラスで同じ名前を持つ別のプライベートvalを再定義できますが、たまたま同じ名前を持つのはまったく異なるvalです。(古いものへのすべての言及は、依然として古いものを参照します。)
aij

1
ただし、クラスのコンテキスト内にいなくても、インタプリタで最終値(または最終値)を作成できるので、これはこのオーバーライド動作だけではないようです。
nairbv 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.