web-dev-qa-db-ja.com

Scalaでは; Appトレイトを使用する必要がありますか?

私はScalaを学習し始めたばかりであり、私が従う多くのチュートリアルはmainメソッドに異なる表現の組み合わせを使用しています。おなじみのメインメソッドは別として。トレイトAppまたはApplicationの使用もあります。Applicationは非推奨であり、推奨されていないようですが、これ以上の説明となる情報は見つかりませんエントリポイントを定義するこれらの方法のそれぞれについて。

だから、誰かが私に説明できるかどうか疑問に思っています:

  • 特性AppApplicationはどのように機能しますか?
  • トレイトApplicationが推奨されなくなったのはなぜですか、またAppトレイトは何が違うのですか?
  • 従来のmainメソッドはどこで使用し、Appを使用してプログラムを開始する必要がありますか? 2つのアプローチの違いは何ですか?
34
Micheal Hill

Application トレイトの問題は、実際にはそのドキュメントに記載されています:

(1)オブジェクトを参照するスレッドコードは、静的な初期化が完了するまでブロックされます。ただし、アプリケーションを拡張するオブジェクトの実行全体は静的初期化中に行われるため、並行コードは、それを囲むオブジェクトと同期する必要がある場合、常にデッドロックになります。

これはトリッキーです。 Applicationトレイトを拡張すると、基本的にJavaクラスを作成します:

class MyApplication implements Application {
  static {
    // All code goes in here
  }
}

JVMは、上記のクラス初期化子をMyApplicationクラスで暗黙的に同期して実行します。これにより、クラスが初期化される前にMyApplicationのインスタンスが作成されないことが保証されます。 MyApplicationのインスタンスに再びアクセスする必要があるアプリケーションからスレッドを生成した場合、プログラム全体が実行された後にのみクラスの初期化が完了するため、アプリケーションはデッドロックになります。これは、プログラムが実行されている限りインスタンスを作成できないため、パラドックスを意味します。

(2)上記のように、Applicationを拡張するオブジェクトの本体のすべてのコードは、Applicationのメインメソッドが実行を開始する前に発生する静的初期化の一部として実行されるため、コマンドライン引数を取得する方法はありません。

クラス初期化子は引数を取りません。また、静的フィールド値を割り当てる前にクラス初期化子を実行する必要があるため、値がクラスに渡される前に最初に実行されます。したがって、argsメソッドで通常受け取るmainは失われます。

(3)静的初期化子はプログラムの実行中に1回だけ実行され、JVMの作成者は通常、実行時間が比較的短いと想定しています。したがって、特定のJVM構成が混乱するか、アプリケーションを拡張するオブジェクトの本体のコードを最適化またはJITできないだけです。これにより、パフォーマンスが大幅に低下する可能性があります。

JVMは、頻繁に実行されるコードを最適化します。これにより、実際にはパフォーマンスのボトルネックではないメソッドの実行時間が無駄にならないことが保証されます。ただし、staticメソッドは手動で呼び出すことができないため、一度だけ実行されると想定しています。したがって、クラス初期化子から実行されるコードは最適化されませんが、mainトレイトを使用している場合は、アプリケーションのApplicationメソッドコードになります。

App トレイトは DelayedInit を拡張することでこれをすべて修正します。この特性は、Scalaコンパイラーに明示的に認識されているため、初期化コードはクラス初期化子からではなく別のメソッドから実行されます。名前のに注意してくださいトレイトの唯一のメソッドへの参照:

trait Helper extends DelayedInit {
  def delayedInit(body: => Unit) = {
    println("dummy text, printed before initialization of C")
    body
  }
}

DelayedInitを実装する場合、Scalaコンパイラは、その実装クラスまたはオブジェクトの初期化コードをfor name関数がdelayedInitメソッドに渡されます。初期化コードは直接実行されません。このように、イニシャライザが実行される前にコードを実行して、Scalaたとえば、アプリケーションのランタイムメトリックをコンソールに出力して、プログラムのエントリポイントと終了をラップします。ただし、 このアプローチのいくつかの警告 があるため、DelayedInitの使用は非推奨です。実際には、Appトレイトによって課される問題を解決するApplicationトレイトのみに依存する必要があります。DelayedInitを直接実装しないでください。

mainで定義する限り、必要に応じてobjectメソッドを定義できます。これは主にスタイルの問題です:

object HelloWorld {
  def main(args: Array[String]) {
    println("Hello, world!")
  }
}
30