6
scala> class A
defined class A

scala> trait B
defined trait B

クラスのオブジェクトを作成すると、次のようになりAます。

scala> new A
res4: A = A@11ea3fc

Aしかし、特性Bが混在するクラスのオブジェクトを作成すると、次のようになります。

scala> new A with B
res3: A with B = $anon$1@172aa3f

ここに匿名のクラスがあります(によってヒントがありanonます)。なんで ?

これは、タイプA with Bが新しいタイプと見なされるためですか(以前は識別子で定義されていませんでした)?

4

2 に答える 2

13

これはA with B、新しいタイプと見なされなければならないという理由だけではありません。Scala型システムの場合、に対応するクラスが存在するかどうかは直接関係ありませんA with B。匿名クラスが生成されるのは、混合されたトレイトのすべてのメソッドのブリッジメソッドが含まれている必要があるためです。

匿名クラスが作成される理由は、オブジェクトにからのすべてのメソッドとからのすべてのメソッドの実装が必要であるためAですB。JVMバイトコードレベルでは、これは複数のクラスを継承することを保証し、多重継承モデルはJVMではサポートされていません。

多重継承(またはミックスイン構成、ただしそれを呼び出したい場合)をシミュレートするために、Scalaはトレイトを作成するときに次のことを行います。

  1. トレイトTにメソッドの実装がない場合は、トレイト内のすべてのメソッドを定義するインターフェイスが作成されます。
  2. トレイトTにメソッド実装がある場合T$classは、の具体的なメソッドごとに静的メソッドを持つクラスを追加で作成しTます。この静的メソッドの本体は、の対応するメソッドと同じですTが、そのシグネチャが変更されてthisパラメータが含まれています。持っていた場合T

    def foo(x: Int) = x
    

その後、次のT$classようになります。

<static> def foo($this: T, x: Int) = x

いくつかのクラスAといくつかのトレイトのミックスイン構成によって取得されたクラスTには、本体を含む静的メソッドに呼び出しを転送する特別なブリッジメソッドが生成されます。このように、メソッドの本体は、が混在するすべてのクラスで複製されるわけではありませんT。これが、匿名クラスを作成する必要がある理由です。で、メソッドごとにブリッジメソッドを定義する必要がありTます。

これが例です。ミックスインコンポジションを実行して新しいクラスを作成する場合、たとえば次のように呼び出しますnew A with T

class A {
  def bar = println("!")
}

trait T {
  def foo(x: Int) = x
}

new A with T

コンパイラはそれを大まかに次のように書き直します。

class A {
  def bar = println("!")
}

<interface> T {
  def foo(x: Int): Int
}

class T$class {
  <static> def foo($this: T, x: Int) = x
}

class $anon extends A <implements> T {
  // notice that `bar` is inherited, but `foo` is not
  <bridge> def foo(x: Int) = T$class.foo(this, x)
}
new $anon

コンパイラは実際にコールサイトを書き直しfooて、ブリッジメソッドではなく、コールサイトから直接静的メソッドを呼び出すことができることに注意してください。そのように行われない理由は、サブタイピングポリモーフィズムをサポートしなくなるためです。

于 2012-06-28T23:26:32.543 に答える
6

はい。タイプがまだである間、A with B両方のインターフェースを実装する基礎となるJavaクラスが必要です。この方法でオブジェクトを何百回も作成すると、おそらく何百ものクラスファイルが作成されることを除けば、これに問題はありません。このような場合は、専用を作成してclass AB extends A with Bからインスタンス化することをお勧めしますnew AB

補足として、特性を直接インスタンス化することもできないことがわかります。たとえば、機能しnew Bません。ここでも明示的なクラスを作成する必要があります。たとえばnew B {}、ここでも合成(「匿名」)クラスになります。

于 2012-06-28T22:38:58.167 に答える