クラスの親子関係と処理の方法について複数の言語で書いてみた

こんにちは!ぐち(@bloguchi)です。

そうだ!クラスの継承について書いてみよう!

という完全に京都に行かんばかりの勢いで思い立ったので、ご紹介します。普段からコード書いてないと忘れちゃってますね。めちゃくちゃ初歩的なことやのに。

実行結果

親クラスで文字列の変数1つとメソッド(関数)を1つ定義して、子クラスにメソッド(関数)1つを定義し、実行時は子クラスのインスタンスから自クラスのメソッドを呼び出し、親クラスのメソッドを呼び出し、親クラスの変数を表示するといった簡単な処理を書きます。

実行結果は共通して下記のようになります。

called subAction()
called mainAction()
foo

Java

まずはjavaからご紹介します。新しいバージョンがリリースされどんどん書き方が変わっていきますが基本的な形は下記のようになるのかなと思います。

ソースコード

public class Main {
    public String str = "foo";

    public void mainAction() {
        System.out.println("called mainAction()");
    }

    public static void main(String[] args) {
        Sub sub = new Sub();
        sub.subAction();
        sub.mainAction();
        System.out.println(sub.str);
    }
}

class Sub extends Main {
    public void subAction() {
        System.out.println("called subAction()");
    }
}

キーワード

  • インスタンス生成
    クラスからインスタンスを生成するのはnewを使います。もちろんインスタンスを格納する変数はクラス型で宣言します。
  • メソッド定義
    メソッドを定義するには、アクセス修飾子、返却値、メソッド名の順に定義します。返却値の有無によって記述を省略することはできず、返却値がない場合でもvoidの宣言が必要です。
  • クラスの継承
    クラスの継承を行うためにextendsを指定し親クラス名を記述します。
  • メソッド呼び出し
    インスタンスからメソッドを呼び出す方法は.(ドット)を使います。
  • 変数定義
    変数を定義する際には格納される値に従ったデータ型を宣言します。

Kotlin

続いてKotlinのコードをご紹介します。

ソースコード

fun main(args: Array<String>) {
    var sub = Sub()
    sub.subAction()
    sub.mainAction()
    println(sub.str)
}

open class Main {
    var str = "foo"

    fun mainAction() {
        println("called mainAction()")
    }
}

class Sub : Main() {
    fun subAction() {
        println("called subAction()")
    }
}

キーワード

  • インスタンス生成
    Javaと異なりインスタンスを生成する際にnewは不要です。
  • 関数定義
    関数を定義するためにfunを宣言します。返却値がある場合は関数名の最後にコロンを付けて型宣言を行います。返却値がない場合は省略可能です。
  • クラスの継承
    クラスを継承させるために親クラスにあたるクラスにはopenを宣言します。Javaでいうところのfinalにあたります。また継承する子クラスの宣言時にはコロンを付け親クラス名を記述します。
  • 関数呼び出し
    インスタンスから関数を呼び出す方法は.(ドット)を使います。
  • 変数定義
    型推論が働くためデータ型の明示的宣言は不要です。varを宣言し変数名を記述します。

Scala

続いてScalaをご紹介します。

ソースコード

object Main extends App{
    var sub = new Sub()
    sub.subAction()
    sub.mainAction()
    println(sub.str)
}

class Main {
    var str = "foo"

    def mainAction() {
        println("called mainAction()")
    }
}

class Sub extends Main {
    def subAction() {
        println("called subAction()")
    }
}

キーワード

  • インスタンス生成
    Javaと同じくnewを使います。
  • 関数定義
    関数を定義するためにdefを使います。返却値がある場合は関数名の最後にコロンを付け返却値のデータ型を宣言します。
  • クラスの継承
    extendsを宣言し、親クラス名を記述します。この辺りはJavaと同じです。
  • 関数呼び出し
    インスタンスから関数を呼び出す方法は.(ドット)を使います。
  • 変数定義
    varを用いて変数を定義しますが、scalaは副作用を嫌う言語ですので可能な限り定数を使うことを推奨しています。そのため不変な変数というと変ですが、上記のコードでいうとstrsubも定数として宣言すべきとなります。その際はvarではなくvalを使って宣言します。

Swift

続いてSwiftです。

ソースコード

class Main {
    var str = "foo"

    func mainAction() {
        print("called mainAction()")
    }
}

class Sub: Main {
    func subAction() {
        print("called subAction()")
    }
}

var sub = Sub()
sub.subAction()
sub.mainAction()
print(sub.str)

キーワード

  • インスタンス生成
    Javaと異なりインスタンスを生成する際にnewは不要です。
  • 関数定義
    関数を定義するためにfuncを使います。返却値がある場合は関数名の最後にコロンを付け返却値のデータ型を宣言します。
  • クラスの継承
    子クラスの宣言時に、コロン付け親クラス名を記述します。親クラスへの何らかの宣言は不要です。
  • 関数呼び出し
    インスタンスから関数を呼び出す方法は.(ドット)を使います。
  • 変数定義
    varを用いて変数を定義しますが、scalaと同様に可能な限り定数を使うことを推奨しています。そのため上記のコードでいうとstrsubも定数として宣言すべきとなります。その際はvarではなくletを使って宣言します。scalaはvalですがswiftはletですのでご注意ください。

JavaScript

続いてJavaScriptのご紹介です。ちょっと記事が長くなってきましたね。

ソースコード

var Main = function() {
    this.str = 'foo';

    this.mainAction = function() {
        console.log('called mainAction()');
    };
}

var Sub = function() {
    this.subAction = function() {
        console.log('called subAction()');
    };
};

Sub.prototype = new Main();
var sub = new Sub();
sub.subAction();
sub.mainAction();
console.log(sub.str);

キーワード

  • インスタンス生成
    クラスからインスタンスを生成するにはnewを使います。名前は似ていても全く関係のない言語Javaと同じです。←余計にややこしいですね。
  • 関数定義
    関数を定義するためにfunctionで宣言します。またクラスのように使われているMainSubfunctionで定義されていますよね。javascriptでは『すべてがオブジェクトである』という思想の元作られているため、これまでの言語のようにクラスや関数といった区分けがされていません。(らしいです。笑)
  • クラスの継承
    さきほどの『すべてがオブジェクトである』という考えから、クラスを継承というよりも機能譲渡に近い処理があります。それがprototypeです。(便宜上)継承したいクラスのインスタンスを自身のprototypeオブジェクトに保持することで連鎖的に親クラスにあたるクラスの機能を使えるようになります。
  • 関数呼び出し
    オブジェクトから関数を呼び出す方法は.(ドット)を使います。
  • 変数定義
    varを用いて変数を定義します。省略して変数を宣言しても動いてしまうあたりがJavascriptのややこしいところですが、きちんと意味があります。varを付けると関数スコープ(変数の有効範囲が関数内)となり、省略するとグローバルスコープ(どこからでも参照可能)となります。ですので変数定義の際は必ずvarをつけるように覚えて頂いて問題ありません。ちなみにscalaやswiftのようにletもありますが、javascriptでは定数とはならず、スコープがより狭くなりブロックスコープとなります。

PHP

続いてPHPです。PHPはけっこう自由な書き方ができ、下記でご紹介する形がすべてではありませんが、僕の好みとしては下記のようになります。

ソースコード

class Main {
    public $str = "foo";

    public function mainAction() {
        print("called mainAction()");
    }
}

class Sub extends Main {
    public function subAction() {
        print("called subAction()");
    }
}

$sub = new Sub();
$sub->subAction();
print(PHP_EOL);
$sub->mainAction();
print(PHP_EOL);
print($sub->str);

キーワード

  • インスタンス生成
    他の多くの言語と同様にnewを使います。時代の流れにより最近はnewをつけずに書ける言語が多くなってきていますのでPHPも将来はどうなるんでしょうね。
  • メソッド定義
    メソッドを定義するにはfunctionを宣言します。返却値があっても宣言時には省略することができます。PHP7から返却値の明示的宣言が導入されましたので当該環境下では関数名の最後にコロンを付け返却値のデータ型を記述してください。
  • クラスの継承
    extendsを宣言し、親クラス名を記述します。この辺りはJavaと同じです。
  • メソッド呼び出し
    多くの言語で採用されている.(ドット)ではなく、->(アロー演算子っていうのかな?)を使います。C言語でよく見る形ですね。
  • 変数定義
    $(ドルマーク)を用いて変数名を記述します。PHPにはデータ型の宣言文がありません。型は自動で定義されています。型推論ではなく自動定義となっています。

Objective-c

最後に一番やっかいなObj-cのご紹介です。コード量がハンパなく多いので説明がめんどくさいのでコードを見て理解頂ければと思います。笑

ソースコード

#import <Foundation/Foundation.h>

@interface Main : NSObject {
    @public
        NSString *str;
}
- (id)init;
- (void)mainAction;
@end
#import "Main.h"

@implementation Main
- (id)init {
    self = [super init];
    if (self != nil) {
        str = @"foo";
    }
    return self;
}

- (void)mainAction {
    printf("called mainAction()\n");
}
@end
#import <Foundation/Foundation.h>
#import "Main.h"

@interface Sub : Main {
}
- (void)subAction;
@end
#import "Sub.h"

@implementation Sub
- (void)subAction {
    printf("called subAction()\n");
}
@end
#import <Foundation/Foundation.h>
#import "Sub.h"

int main(void){
    Sub *sub = [[Sub alloc] init];
    [sub subAction];
    [sub mainAction];
    printf(sub->str);
    return 0;
}

キーワード

  • インスタンス生成
    他の言語とは一味違い、allocで領域を確保しinitで初期化を行います。このあたりはC言語といいますかSmallTalkちっくと言うべきでしょうか。
  • メソッド定義
    他の言語でいうところのpublicやprivateなどのアクセス修飾子は+で宣言します。ヘッダファイルで定義したものを実装ファイルにて処理を記述します。
  • クラスの継承
    ヘッダファイルにて子クラスの宣言時にコロンを付けて親クラス名を記述します。
  • メソッド呼び出し
    Obj-cはメッセージ式と呼ばれる表記を用いており、[]を使ってメソッドを呼び出します。
  • 変数定義
    ヘッダファイルにてスコープを定義して変数を宣言します。プロパティなどを使えばgetterやsetterを別途定義する必要もなく自動で作られます。

まとめ

プログラミングでいえばクラス定義や関数の使い方など超基本的なことですが、日頃からコーディングしておかないと忘れちゃいますね。今回はとある言語の参考書をちらちら見ていた際に、ふと思いついて各種言語で書いてみましたが、意識的に日常的に書いていかないといけないなと痛感しました。

PG工程から離れたといっても、この業界にいる限りは避けては通れない部分でしょうし、まだまだ若手に負けるわけにもいきませんしね。笑

では今回はこの辺で。