白猫のメモ帳

C#とかJavaとかJavaScriptとかHTMLとか機械学習とか。

Java使いがC#を勉強する その⑤ 継承とインタフェース

こんばんは。

今回はクラスとインタフェースについて比較します。
オブジェクト指向なので、だいたいクラスでしょといってしまえばおしまいなのですが、
それはそれということで。

命名規則


クラスはJavaC#パスカル記法(キャメルケースの先頭も大文字)を使います。

public class Piyo {}


インタフェースもパスカル記法ですが、C#では先頭に「I」を付けます。
個人的にはこれはあまり好きじゃないですね。

public interface IPiyo {}

継承


Javaではクラスの継承とインタフェースの実装は区別されますが、

public class Hoge extends Fuga implements Piyo {}

C#では特に区別はありません。

public class Hoge : Fuga, IPiyo {}


継承に関する修飾子


abstract

abstract修飾子はクラス、メソッドなどが抽象的なものであることを表します。
abstractメソッドはabstractクラスにしか定義できません。
文法としてはJavaC#も同じです。

abstract class ParentClass
{
  abstract void Method();
}

abstractクラスは自身をインスタンス化できません。
インスタンス化するためにはこれを継承したクラスを作る必要があります。

継承したクラスがabstractでない場合、
親クラスのすべてのabstractメソッドを実装しなければなりません。

class ChildClass : ParentClass
{
  override void Method()
  {
    Console.WriteLine("ほげ");
  }
}

virtual

virtual修飾子はメソッドなどが派生クラスでオーバーライド可能であることを示します。
Javaではfinalを付けなければオーバーライド可能なので、
基本的にvirtual修飾子がついているようなイメージですね。

class ParentClass
{
  virtual void Method()
  {
    Console.WriteLine("ほげ");
  }
}

class ChildClass : ParentClass
{
  override void Method()
  {
    Console.WriteLine("ふが");
  }
}

abstractは実装なしで派生クラスに実装を促し、
virtualは実装ありで派生クラスで上書きできるということです。

sealed

sealed修飾子はクラスの継承を禁止します。
Javaではfinalのついたクラスに相当します。

sealed class ParentClass {}
class ChildClass : ParentClass{} // エラー

メソッドはもともとvirtual(かabstract)がないとオーバーライドできませんが、
派生クラスでオーバーライドしたうえで、さらにその派生クラスでのオーバーライドを防ぐ目的で、
メソッドにもsealed修飾子を付けることができます。

class ParentClass
{
  public virtual void Method() {}
}

class ChildClass : ParentClass
{
  public sealed override void Method() {}
}

class GrandchildClass : ChildClass 
{
  // エラー
  public override void Method() {}
}

new

new修飾子は継承されたメソッドなどを明示的に隠蔽できます。
また、new修飾子を付けなくても隠蔽は可能ですが、コンパイルの警告が出ます。

class ParentClass
{
  public void Method() {}
}

class ChildClass : ParentClass
{
  public new void Method() {}
}

一見するとoverrideと同じように見えますが、
拡張ではなく隠蔽なので、少し挙動が異なります。

class A 
{
  public virtual void Method() 
  {
    Console.WriteLine("A");
  }
}

class B : A 
{
  public override sealed void Method() 
  {
    Console.WriteLine("B");
  }
}

class C : B 
{
  public new void Method() 
  {
    Console.WriteLine("C");
  }
}

↑こんな風にすると、こうなる。↓

A a = new A();
A b = new B();
A c = new C();

a.Method(); // A
b.Method(); // B
c.Method(); // B

つまり、overrideはインスタンスの型で呼び出されるメソッドが変わり、
newは変数の型で呼び出されるメソッドが変わるわけですね。

ちなみにsealedを付けても隠蔽はできます。

多重継承的ななにか


JavaにもC#にも多重継承はないですが、似たようなことができる機能があります。

Javaの場合にはインタフェースのデフォルト実装を利用します。

public interface Piyo {
  default String piyo() {
    return "ぴよ";
  }
}


一方でC#では拡張メソッドを利用します。

public interface IPiyo {}

public class Util
{
  static string Piyo(this IPiyo piyo) {
    return "ぴよ";
  }
}

拡張メソッドは既存のクラス、インタフェースなどに好きにメソッドが追加できて便利ですが、
実装したいインタフェース自体に定義が書けないのがなんだか混乱しそうです。そうでもない?

拡張メソッドについては次回もう少し詳しく見ていこうかとおもいます。

 

なりますまし許すまじ


アクセス修飾子はまたの機会に。
volatileとかtransientとかstrictpfはまぁいいかな。
staticは・・・まぁいいかな。

個人的にはオーバーライドするときに上のクラスの変更とかしたくないので、
断りがなければ継承できるJavaスタイルの方が好きなのですけれど。むぅ。

new修飾子がなんだかとても難しいです。
これはポリモーフィズムを破壊したりしないの?
うーん。うーん。