1

私の質問は、タイトルが言っていることのほとんどです:明示的な型キャストを許可しないプログラミング言語を持つことは可能ですか?

Base言いたいことを明確にするために、親クラスと子クラスを持つ C# のような言語で作業していると仮定しますDerived。明らかに、そのようなコードは安全です:

Base a = new Derived();

継承階層が上がるので安心ですが、

Dervied b = (Base)a;

降りるのは安全ではないため、安全は保証されません。

ただし、安全性に関係なく、このようなダウンキャストは多くの言語 (Java や C# など) で有効です。コードはコンパイルされ、型が正しくない場合は実行時に失敗します。したがって、技術的には、コードは依然として安全ですが、コンパイル時チェックではなく実行時チェックを介して行われます (ちなみに、私は実行時チェックのファンではありません)。

個人的には、少なくとも理論的な観点から、せいぜい信頼できるコードの観点から、コンパイル時の完全な型安全性が非常に重要であると考えています。コンパイル時の型安全性の結果として、キャストは不要になります (これは素晴らしいことだと思います。とにかく醜いからです)。キャストのような動作は、暗黙的な変換演算子またはコンストラクターによって実装できます。

だから私は疑問に思っています.現在、キャストが時代遅れであるほど厳密なタイプの安全性をコンパイル時に提供するオブジェクト指向言語はありますか? つまり、安全でない変換操作は一切許可されていませんか? それとも、これがうまくいかない理由がありますか?

ご意見ありがとうございます。

編集

例によって明確にすることができれば、これが私がダウンキャストをとても嫌う大きな理由です。

私が次のものを持っているとしましょう(大まかにC#のコレクションに基づいています):

public interface IEnumerable<T>
{
     IEnumerator<T> GetEnumerator();
     
     IEnumerable<T> Filter( Func<T, bool> );
}

public class List<T> : IEnumerable<T>
{
    // All of list's implementation here
}

ここで、誰かが次のようなコードを書くことにしたとします。

List<int> list = new List<int>( new int[]{1, 2, 3, 4, 5, 6} );
// Let's filter out the odd numbers
List<int> result = (List<int>)list.Filter( x => x % 2 != 0 );

最後の行でキャストが必要であることに注意してください。しかし、それは有効ですか?一般的ではありません。List<T>.Filter確かに、 の実装が別の を返すことは理にかなっていますList<T>が、これは保証されていません ( の任意のサブタイプである可能性がありますIEnumerable<T>)。ある時点でこれが実行されたとしても、後のバージョンでこれが変更される可能性があり、コードがいかに脆弱であるかが明らかになります。

ダウンキャストが必要だと私が考えることができるほとんどすべての状況は、この例のようなものになります。メソッドにはクラスまたはインターフェイスの戻り値の型がありますが、実装の詳細がいくつかわかっているため、結果をダウンキャストすることに自信があります。しかし、OOP は実装の詳細からの抽象化を実際に促進するため、これは反 OOP です。では、純粋な OOP 言語であっても、なぜそれを行うのでしょうか?

4

3 に答える 3

2

型システムの能力を向上させることで、ダウンキャストを徐々に排除することができます。

あなたが与えた例に対する提案された解決策の1つは、メソッドの戻り値の型を「これと同じ」と宣言する機能を追加することです。これにより、サブクラスはキャストを必要とせずにサブクラスを返すことができます。したがって、次のようなものが得られます。

public interface IEnumerable<T>
{
 IEnumerator<T> GetEnumerator();

 This<T> Filter( Func<T, bool> );
}

public class List<T> : IEnumerable<T>
{
    // All of list's implementation here
}

キャストは不要になりました。

List<int> list = new List<int>( new int[]{1, 2, 3, 4, 5, 6} );
// Compiler "knows" that Filter returns the same type as its receiver
List<int> result = list.Filter( x => x % 2 != 0 );

ダウンキャストの他のケースでも、型システムを改善することによる解決策が提案されていますが、これらの改善は C#、Java、または C++ にはまだ行われていません。

于 2012-11-07T01:18:42.783 に答える
1

確かに、サブタイプをまったく持たないプログラミング言語を使用することは可能です。その場合、当然、ダウンキャストは必要ありません。ほとんどの非オブジェクト指向言語は、そのクラスに分類されます。

Java のようなクラスベースの OO 言語でさえ、基本クラスにメソッドを持たせるだけで、ほとんどのダウンキャストを正式に置き換えることができます。

Foo meAsFoo() {
   return null;
}

サブクラスはそれをオーバーライドしてそれ自体を返します。ただし、これは実行時テストを表現する別の方法にすぎず、使用がより複雑になるという欠点があります。そして、継承ベースのサブタイピングの他のすべての利点を失うことなく、パターンを禁止することは困難です。

もちろん、これは親クラスを変更できる場合にのみ可能です。それをプラスと考えるかもしれませんが、親クラスを変更して回避策を使用できる頻度を考えると、「良い」設計を奨励するという点でどれだけの価値があるかわかりません (多かれ少なかれ「良い」の任意の値)。

言語がダウンキャスト式の代わりにケース マッチング構造を提供する場合、より安全なプログラミングを促進するケースを作成できます。

Shape x = .... ;
switch( x ) {
  case Rectangle r:
    return 5*r.diagonal();
  case Circle c:
    return c.radius();
  case Point:
    return 0 ;
  default:
    throw new RuntimeException("This can't happen, and I, "+
            "the programmer, take full responsibility");
}

しかし、閉じた世界の仮定 (現代のプログラミング言語は作るのをためらうようです) がなければ、これらの切り替えの多くは、プログラマーが絶対に起こり得ないことを知っているdefault:場合を必要とし、プログラマーの感覚を鈍らせる可能性があるため、実際には問題になる可能性があります。結果のスローに。

于 2011-08-20T23:34:47.980 に答える
0

ダックタイピングや暗黙の型変換を行う言語はたくさんあります。Perl が思い浮かびます。スカラー型のサブタイプが内部でどのように変換されるかという複雑さは、頻繁に批判の対象となりますが、期待どおりに動作する場合、言語の DWIM 感に貢献するため、賞賛も受けます。

従来の Lisp は別の良い例です。アトムとリストだけがあり、nil両方が同時に存在します。そうでなければ、双子は決して会うことはありません...

(ただし、プログラミング言語が必然的にオブジェクト指向で、厳密に型指定され、コンパイルされる宇宙から来たようです。)

于 2011-09-10T16:36:53.927 に答える