「この継承は変な感じ」
気持ち悪い理由。…Point3D じゃなく Poit3D だから、というのは嘘で :D (以降勝手に修正して書きます)
Point3D extends Point2D としたとき、たとえば Point3D.moveTo() というメソッドは
class Point3D extends Point2D { void moveTo (double x, double y, double z) { ... } }
と、3引数を取るべきで、単純なオーバーライドではうまくいかなくなります。他の、Point2D オブジェクトを操作していたメソッドには、一様にこの問題が発生する気が。
ColoredPoint2D extends Point2D では、こういう問題は起こらないです。たぶん。
「こういう型をクラスで定義したいんだけど」
はて何だろう。
うまく定義できるか、という問いに答えてませんが、NonNegativeInteger を定義すると、引き算オペレータとかで結果が負になる(NonNegativeInteger で扱える範囲を超える)場合どうするんですかね。
「こういう型達の関係は」
unionなりvariant型なりが使えるなら、そっちを使うのが筋なんでしょうけど。
この場合は、number と handle の扱いは次のようにするとか。
- Number か Handle かのどちらかをとる列挙型 ID を持つメンバ which を加える
- ID型の1引数を取るコンストラクタを定義し、これを使って、そのインスタンスの which の値を決定する。
- アクセッサメソッドに which の判定を仕込む事で、セットの制限を行う。
UserNumber と UserHandle は、コンストラクタでbaseクラスのIDを自動的にセットするように仕込むようにして、後は普通に継承。
実際に、C#でコード書いてみました。重要なところだけ抜粋。
/// <summary> /// number か Handleかを決定する列挙型 /// </summary> enum ID { Number, Handle }; /// <summary> /// UserID クラス /// </summary> class UserID { protected int number; protected String handle; protected ID which; /// <summary> /// number へのアクセッサメソッド /// </summary> public virtual int Number { get { if (which == ID.Number) return number; else Error(); // Error() の中身は適当に } set { if (which == ID.Number) number = value; else Error(); } } /// <summary> /// handle へのアクセッサメソッド /// </summary> public virtual String Handle { get { if (which == ID.Handle) return handle; else Error(); } set { if (which == ID.Handle) handle = value; else Error(); } } /// <summary> /// コンストラクタ /// </summary> /// <param name="select">IDの設定</param> public UserID(ID select) { which = select; } /// <summary> /// IDを表示する。 /// </summary> public void Show() { switch (which) { case ID.Number: Console.WriteLine("Number: " + number.ToString()); break; case ID.Handle: Console.WriteLine("Handle: " + handle); break; }; } } /// <summary> /// UeerNumberクラス::コンストラクタの定義のみ。 /// </summary> class UserNumber : UserID { public UserNumber() : base(ID.Number) { } } /// <summary> /// UserHandleクラス::コンストラクタの定義のみ。 /// </summary> class UserHandle : UserID { public UserHandle() : base(ID.Handle) { } } /// <summary> /// Mainプログラム /// </summary> class Program { static void Main(string[] args) { UserHandle handleID = new UserHandle(); handleID.Handle = "Takeo"; UserNumber numberID = new UserNumber(); numberID.Number = 5; handleID.Show(); numberID.Show(); } }