それらがオーバーライドされているにもかかわらず、スーパークラスの方法のみを強制する方法(OCAML)
-
26-10-2019 - |
質問
正直に言うと、OCAMLのオブジェクトシステムについてはあまり知りません。次のスニペットは私の問題を示しています:
class foo =
object (o)
method foo1 y =
print_endline "foo1";
o#foo2 (y - 1)
method foo2 y =
print_endline "foo2";
if y > 0 then o#foo2 y else print_endline "end"
end
class bar =
object (o : 'self_type)
inherit foo as super
method foo2 y =
print_endline "bar2";
if y > 0 then super#foo1 y else print_endline "endbar"
end
let _ = (new bar)#foo2 3
実行すると、スニペットは次の出力を生成します。
bar2
foo1
bar2
foo1
bar2
endbar
, 、SuperClassメソッドFOO1(Super#FOO1を介して呼び出される)が、サブクラスからOverridenメソッドFOO2を実行することを示しています。代わりに、スーパークラスからのメソッドfoo2を呼び出すことを期待していたでしょう。
この動作を達成することは可能ですか?つまり、サブクラスでオーバーライデンであっても、他のスーパークラスメソッドのみを呼び出すスーパークラスメソッドがありますか?
解決
私はこれについて100%確信していませんが、あなたはできないと確信しています。 OCAMLの継承とサブタイピングは、2つの異なる概念です。クラスは、継承なしに別のタイプのサブタイプにすることができます。継承はすべて、継承クラスからメソッドを引き込むことです。
多型は構造タイピングを介して達成されるため、 foo2
メソッドをに呼び出します bar
なぜなら bar
実装しています foo2
そして、からではありません bar
継承します foo
(言っているように、C ++、どこで bar#foo2
仮想関数テーブルを検索するために呼び出されます)。
とはいえ、OCAMLは、オーバーライドされた方法とを使用して継承された方法を区別する方法を提供します。 inherit...as...
構文。の bar
あなたの例から、 o#foo1
と super#foo1
同じ方法です(以降 bar
実装していません foo1
) 一方 o#foo2
と super#foo2
異なる方法です。それにもかかわらず、継承されたクラスがそれが継承されていることを知ることができ、その方法とオーバーライドされた方法を区別することはとにかくないと思います。そのためにはいくつかの構文があるかもしれませんが、継承と多型が独立した概念であるという事実のために、私はそれを非常に疑っています。
他のヒント
いいえ、ちがいます。これは、OCAMLに固有のものではなく、OOの基本的なアイデアである「後期バインディング」です。
私はあなたが何をしようとしているのかわかりませんが、オブジェクト指向のプログラミングはおそらく適切なツールではなく、少なくともそのように使用されません。 OCAMLを学びたい場合は、おそらく開始するために十分に興味深い非OO部品に集中する必要があります。
Gascheが指摘しているように、これはOO言語の意図された標準的な動作です。
super#foo1
それ以来、電話してください bar
オーバーライドしません foo1
, 、まったく同等です o#foo1
. 。 super
コンストラクトは、呼び出すことができるようにのみ存在します foo2
の方法 foo
メソッドから bar
(それ以外の場合、その方法を参照する方法はありません)。の foo1
と呼ばれるように bar#foo2
, o
実際にはです bar
, 、ではありません foo
, 、だから呼び出します foo2
メソッドはを呼び出します bar
foo2
方法。
その動作をハードコードしたい場合は、オブジェクト指向のプログラミングを使用しないでください。他の関数を呼び出す関数としてそれを実装するだけです。
(私が理解しているように「その行動」:電話する場合 foo2
ASと呼ばれているコード内から super#foo1
, 、それから正確に foo2
スーパークラスの実装から、サブクラスからのより「具体的な」実装からではなく、呼び出されるべきです)
それは、最もシンプルでクリーンで最も明確な進行方法です。あなたが望むことを行うプログラム関数です。
または、自分自身と私たちに説明する必要があります。なぜOOPが必要なのですか?その理由は、質問テキストには示されていません。なぜ作る foo1
と foo2
独立した機能ではなく方法? (以外に foo1
と foo2
, 、必要に応じて、プログラムにいくつかのオブジェクトとクラスとメソッドがある場合があります)。
この質問が他のlnaguagesとの比較から来ているかどうか疑問に思う
別のOO言語を知っている場合、それはOOPから「その動作」が欲しいのは奇妙です。たとえば、JavaやC ++で期待される動作ではありません。実行時、プログラムでメソッドを呼び出すと、実行時に実際にオブジェクトに関連付けられているそのメソッドの実装に発送されます。だから、要するに: プログラムでメソッドコール式を使用するときはいつでも、メソッドの実装を見つけるというこの原則にコミットします(「後期バインディング」), 、Gascheが指摘したように。まだcf。 Niki Yoshiuchiが指摘した仮想メソッドテーブルで実装されたOCAMLと言語の違い。
利用可能および募集行動のすべての議論を正式化する
あなたが望むものは、多くの一般的なOO言語で予想され、利用可能な動作ではないかもしれませんが、OOP実装の内部にアクセスできる場合、それは想像できるものであり、特定のOOPシステムで実装可能である可能性があります。
いくつかの実装で、 super
スーパークラスのメソッドテーブルを保持する構造(現在のオブジェクトのメソッド呼び出しを解決するときにフォールバックする)であり、メソッドは最初のargとしてオブジェクト(メソッドテーブル)を受信する必要がある関数であり、次にあなたを実行するために欲しい、書くだろう super.foo1(super, y)
.
(実際、内部がプログラマーにさらされ、そのような電話をかけることができるOOP実装があるのだろうか。)
一方、通常のOOPの動作は、このシステムで表現されます。 this.foo1(this, y)
(どこ this
現在のオブジェクトのメソッドテーブルです。)
あなたのocamlコール super#foo1 y
またはJava super.foo1(y);
この「私の」システムに変換されます super.foo1(this, y)
. 。 (まだcf。ocamlとjavaのような言語との間の吉井のニキが指摘した違いは、仮想メソッドテーブルで実装されています。)
3つのケースの違いがわかります。
付録。そのように機能する言語を探しています
うーん、PHPはその動作が可能になる言語かもしれませんが:
- 「クラスレベル」プログラミング(静的メソッド)でのみ、オブジェクトレベルではありません。
- いくつかの奇妙なコードをハードコードするときのみ 「後期静的結合」 関数呼び出しで:
#!/usr/bin/php
<?php
class Foo {
public static function foo1($y) {
echo "foo1\n";
static::foo2($y-1);
}
public static function foo2($y) {
echo "foo2\n";
if ($y > 0)
static::foo2($y);
else
echo "end\n";
}
}
class Bar extends Foo {
public static function foo2($y) {
echo "bar2\n";
if ($y > 0)
Foo::foo1($y);
else
echo "endbar\n";
}
}
class Model extends Foo {
public static function foo2($y) {
echo "model2\n";
if ($y > 0)
static::foo1($y);
else
echo "endmodel\n";
}
}
Model::foo2(3);
Bar::foo2(3);
?>
モデルは、仮想メソッドを備えた標準的なOOオブジェクトのように動作し、 あなたは欲しかった:
$ ./test-force-super-binding.php | head -20 model2 foo1 model2 foo1 model2 foo1 model2 endmodel bar2 foo1 foo2 foo2 foo2 foo2 foo2 foo2 foo2 foo2 foo2 foo2 $
(ところで、使用 parent::
それ以外の Foo::
あなたの望ましい行動に私たちを連れて行かないでしょう。)
私は、非常識なPHPの結合仕様のプラポースを理解していません static::
, 、静的な方法(つまり、クラスレベルのプログラミング)に対してのみ効果があります。
類似のC ++の例では、デフォルトでOOオブジェクトレベルの動作が得られません。
#include<iostream>
class Foo {
protected:
static void foo1(int y) {
std::cout << "foo1" << std::endl;
foo2(y-1);
}
public:
static void foo2(int y) {
std::cout << "foo2" << std::endl;
if (y > 0)
foo2(y);
else
std::cout << "end" << std::endl;
}
};
class Bar: public Foo {
public:
static void foo2(int y) {
std::cout << "bar2" << std::endl;
if (y > 0)
foo1(y);
else
std::cout << "endbar" << std::endl;
}
};
int main() {
Bar::foo2(3);
return 0;
}
- それはあなたの指名手配行動を与えます:
$ ./a.out | head -10 bar2 foo1 foo2 foo2 foo2 foo2 foo2 foo2 foo2 foo2 $
コードの関数呼び出しに特別な修飾子がなくても Bar::foo2()
, 、だから私たちにとって面白くない。
Javaの静的方法についてはどうですか?..(それらはC ++とは異なり、デフォルトで機能コールの仮想メソッドのような解像度を提供しますか?)