SCALA関数の分散とオーバーライド
-
28-09-2019 - |
質問
過負荷時にメソッドの分散を理解するのが少し問題があります。
これは返品タイプの共分散のために完全に機能しますが
class Bla
class Fasel extends Bla
trait Test[A] {
def tester(): Bla = new Bla
}
class FooTest[A](a: A) extends Test[A] {
override def tester(): Fasel = new Fasel
}
機能がパラメータータイプに違反している場合でも、これは失敗します。
class Bla
class Fasel extends Bla
trait Test[A] {
def tester(a: Fasel): Bla = new Bla
}
class FooTest[A](a: A) extends Test[A] {
override def tester(a: Bla): Fasel = new Fasel
}
ここで何が間違っているのですか?ポインターはありますか?
よろしく、ライチョ
解決
ここでは2つのことが起こっています。
- 関数とメソッド 同じものではありません
- 方法は、パラメーターのタイプでは多型ではありません
あなたの tester
方法はメソッドです、 ではありません Function1
. 。アンダースコア構文を使用して、関数に持ち上げることができます。
val f = (new FooTest[String]).tester _ // Fasel => Bla
この関数は、入力タイプが対照的になります。 (しかし、それは機能すると言う価値があります パラメーター化することはできません また、私はのインスタンスを持っていなければならなかったと言う価値があります Foo
また FooTest
の関数オブジェクトを取得するため tester
方法。もちろん、これは最初の観察から続きます!)
関数はオブジェクトです、 オーバーライドすることはできません それは意味がありません。メソッドはオーバーライドできます。ただし、上で述べているように、オーバーライドはメソッドのパラメータータイプでは多型ではありません。したがって、例:
class A {
def foo(a : Any) = println("A: " + a)
}
class B extends A {
override def foo(s : String) = println("B " + s) //will not compile!
}
上記の私の例の2つの方法は、2つの個別の方法です。動的ディスパッチは、メソッドターゲット(つまり、呼び出されているオブジェクト)でのみ機能します。
上記の例では、削除した場合 override
宣言、コードはコンパイルされます。以下を実行した場合:
(new B).foo(1) //prints A 1
(new B).foo("s") //prints B s
これは、両方の方法が呼ばれているからです foo
, 、それらは完全に異なる方法です(つまり、私は持っています 過負荷 foo
, 、 いいえ オーバーライド それ)。メソッドの引数(タイプを含む)がそのメソッドの一意の一部を形成することであると理解されているのが最もよく理解されています 名前. 。 1つの方法は、まったく同じものを持っている場合にのみ別の方法を無効にします 名前.
基本的に、あなたは2つのものを混乱させました 分離されていない あなたの質問の中のこと、私は明確にするために置きます:
- 分散注釈
Function1
ある関数が別の機能のサブタイプであることの意味を定義します(したがって 割り当て可能 特定のタイプの参照)。 - メソッドはサブクラスでオーバーライドでき、言語仕様は、そのようなオーバーライドがいつ行われるかのルールの概要を説明します。
他のヒント
仕様の関連するスニペット:
メソッドタイプ
メソッドタイプは内部で示されます
(Ps)U
、 どこ(Ps)
パラメーター名とタイプのシーケンスです(p1 :T1,...,pn :Tn)
いくつかのためのn≥0
とU
(値または方法)タイプです。このタイプは、名前が付けられた引数を取得する名前のメソッドを表しますp1, ..., pn
タイプのT1,...,Tn
そして、それはタイプの結果を返しますU
.メソッドタイプは、値のタイプとして存在しません。メソッド名が値として使用される場合、そのタイプは対応する関数タイプ(§6.26)に暗黙的に変換されます。
オーバーライド
メンバー
M
クラスのC
それ マッチ (§5.1.3)非プライベートメンバーM′
のベースクラスのC
そのメンバーを無効にすると言われています。この場合、オーバーライドメンバーの結合M
しなければならない サブセム (§3.5.2)オーバーライドされたメンバーの結合M′
.
適合
もしも
Ti ≡ Ti′
にとってi = 1, ..., n
とU
に適合しますU′
次に、メソッドタイプ(p1 : T1,...,pn :Tn)U
に適合します(p1′ :T1′,...,pn′ :Tn′)U′
.
包着
いくつかの複合タイプのクラスタイプの宣言または定義
C
いくつかの複合タイプまたはクラスタイプで同じ名前の別の宣言を包含するC′
、次のいずれかが保持されている場合。
- タイプtの名前xを定義する値宣言または定義は、定義する値またはメソッド宣言を包含する
x
タイプ付きT′
, 、 提供されたT <: T′
.
リターンタイプをサブタイプにオーバーライドして変更できますが、引数のスーパータイプを受け入れると代替原則を満たすことができますが、それは許可されません(これはJavaと同じです)。名前、異なる引数数とタイプ)とあなたの方法は過負荷と考慮されます。これは主にJVMの互換性と合理的な仕様を持っていることの問題だと思います。過負荷はすでにScala仕様をかなり複雑にします。 Overridenメソッドを変更した署名でオーバーロードされた方法にルーティングするだけで、十分に優れている可能性があります。
class FooTest[A] extends Test[A] {
override def test(a: Fasel) : Fasel = test(a.asInstanceOf[Bla])
def test(a: Bla) : Fasel = new Fasel
}
あなたができることは、違反した位置でのみ表示されるタイプパラメーターに違反することです(単純化、結果タイプとしてではなく、引数タイプとして表示されます)が、それはまったく異なります:
trait Test[-A] {
// note the - before A.
// You might want to constraint with -A >: Fasel
def tester(a: A) : Bla = new Bla
}
class FooTest extends Test[Bla] {
override def tester(a: Bla): Fasel = new Fasel
}
val testOfBla: Test[Bla] = new FooTest
val testOfFasel: Test[Fasel] = testOfBla
// you can assign a Test[Bla] to a test[Fasel] because of the -A
2番目の例では、の署名です tester()
の Test
宣言します Fasel
議論ですが、オーバードリデンの署名があります FooTest
tester()
で宣言されています Bla
議論として。以来 Fasel
のサブタイプです Bla
彼らによって extend
s階層これはおそらく間違っています。