ITコンサルの日常

ITコンサル会社に勤務する普通のITエンジニアの日常です。

親クラスのメソッドをサブクラスで上書きしたらどうなるか。

どうなるか@Java

public class Override
{
        public static void main(String[] args)
        {
                MyTest mt = new YourTest();
                mt.hello();
        }
}

class Test
{
        public void hello()
        {
                System.out.println("Test#hello()");
        }
}

class MyTest extends Test
{
        public void hello()
        {
                System.out.println("MyTest#hello");
        }
}

class YourTest extends MyTest
{
        public void hello()
        {
                System.out.println("YourTest#hello");
        }
}

実行した結果は、

YourTest#hello

となります。
この短いコードだとあんまり実感ないですが、素直に見ればMyTest#helloを呼んでいる(ように見える)のに、実際はYourTest#helloが呼ばれます。
じゃあ、C#で同じようなことをやるとどうなるかというと、

using System;

namespace test
{
        class Override
        {
                static void Main()
                {
                        MyTest mt = new YourTest();
                        mt.hello();
                }
        }

        class Test
        {
                public void hello()
                {
                        Console.WriteLine("Test#hello");
                }
        }

        class MyTest : Test
        {
                public void hello()
                {
                        Console.WriteLine("MyTest#hello");
                }
        }

        class YourTest : MyTest
        {
                public void hello()
                {
                        Console.WriteLine("YourTest#hello");
                }
        }
}

コンパイル時にワーニングが出ます。

>csc Override.cs
Microsoft (R) Visual C# 2008 Compiler version 3.5.21022.8
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.

Override.cs(24,15): warning CS0108: test.MyTest.hello()' は継承メンバ
        'test.Test.hello()' を隠します。意図的に隠す場合はキーワード new
        を使用してください。
Override.cs(16,15): (以前のエラーに関連する警告の位置)
Override.cs(32,15): warning CS0108: test.YourTest.hello()' は継承メンバ
        'test.MyTest.hello()' を隠します。意図的に隠す場合はキーワード new
        を使用してください。
Override.cs(24,15): (以前のエラーに関連する警告の位置)

要は勝手に上書きすんなってことですね。これは親切な気がします。さっきのJavaで示した例のような誤解を招くコードは防げますね。(実際やるかどうかは別として。)
ちなみに、上のメッセージに出ているように、キーワードnewを付加すれば、上書きすることが出来ます。
キーワードnewを付けても付けなくても、この結果はこうなります。

MyTest#hello

コード通り、MyTest#helloが呼ばれてしまいました。。
C#C++と同じく、仮想関数定義にしないとポリモーフィズムにならないようです。
ので、こんなvirtualキーワードを付け加えてこうします。

using System;

namespace test
{
        class Override
        {
                static void Main()
                {
                        MyTest mt = new YourTest();
                        mt.hello();
                }
        }

        class Test
        {
                public virtual void hello()
                {
                        Console.WriteLine("Test#hello");
                }
        }

        class MyTest : Test
        {
                public override void hello()
                {
                        Console.WriteLine("MyTest#hello");
                }
        }

        class YourTest : MyTest
        {
                public override void hello()
                {
                        Console.WriteLine("YourTest#hello");
                }
        }
}

この結果はこうです。

YourTest#hello

今度はちゃんとポリモーフィズムしてくれました。このソースは最初のJavaソースと同じ動きになりますね。
ちなみに、YourTest#helloをoverrideキーワードの代わりにnewキーワードとすることも出来ます。
例えばこんな感じ。

using System;

namespace test
{
        class Override
        {
                static void Main()
                {
                        YourTest yt = new YourTest();
                        yt.hello();

                        Test test_yt = yt;
                        test_yt.hello();

                        MyTest mytest_yt = yt;
                        mytest_yt.hello();
                }
        }

        class Test
        {
                public virtual void hello()
                {
                        Console.WriteLine("Test#hello");
                }
        }

        class MyTest : Test
        {
                public override void hello()
                {
                        Console.WriteLine("MyTest#hello");
                }
        }

        class YourTest : MyTest
        {
                public new void hello()
                {
                        Console.WriteLine("YourTest#hello");
                }
        }
}

この結果はこう。

YourTest#hello
MyTest#hello
MyTest#hello

うーん。分かりづらい。
結局何が言いたかったかっていうと、C#多機能なのはいいがややこしい!っていうところでしょう。

  • 基本、多態性を持たせたいメソッドはvirtualで宣言し、サブクラスでoverrideする。
  • 多態性を持たないメソッドは、各クラスで個別に名前を付ける。決してnewキーワードは使ってはならない。

がベストかなぁと思います。