2011年2月2日水曜日

Objective-C(メソッドのオーバーロード)

Objective-CもC++やJavaと同様にオーバーロードが存在しますがはオーバーロードっぽいことが出来ますが、言語仕様としてのオーバーロードは存在しません。本記事を見るとラベル名を変更すればオーバーロード出来ると記載してますが、Objective-Cでラベル名を変更するということは、異なるメソッドを定義している事にすぎません。
Reference : Method overloading in Objective-C?(stackoverflow)
(誤解を招く記載のため修正 2014/02/08 ナヨユキ)

引数の数が異なるメソッドのオーバーロード

単純な例を示します。クラスAが存在し、それぞれ引数の数が異なるメソッドを3つ用意し、main関数内でぞれぞれの引数が異なるメソッドを呼び出してみます。

/*************/
/* class A ***/
/*************/
@interface A : NSObject {
}
- (int) test1;
- (int) test1: (int) a1;
- (int) test1: (int) a1 arg2: (int) a2;

@end;

@implementation A 
    //オーバーロードメソッド1(引数無し)
    - (int) test1 {
        printf("wow1.\n");
    }

    //オーバーロードメソッド2(引数1つ)
    - (int) test1: (int) a1 {
        printf("wow2. arg1 = %d.\n", a1); 
    }

    //オーバーロードメソッド3(引数3つ)
    - (int) test1: (int) a1 arg2: (int) a2 {
        printf("wow3. arg1 = %d. arg2 = %d\n", a1, a2);
    }
@end

/**********/
/* main ***/
/**********/
int main(void) {
    A* classA = [[A alloc] init];
    [classA test1];              //引数無し
    [classA test1: 1];           //引数1つ
    [classA test1: 1 arg2: 2];   //引数2つ

    return 0;
}

実行結果

wow1.
wow2. arg1 = 1.
wow3. arg1 = 1. arg2 = 2

それぞれ引数の異なるメソッドを正しく呼び出せています。引数の数が異なる場合は問題なくオーバーロードが出来るようです。このあたり、Java/C++とは同じだと思います。

引数の型が異なるメソッドのオーバーロード

次に引数の型が異なるメソッドを定義してみます。

/*************/
/* class A ***/
/*************/
@interface A : NSObject {
}
- (int) test1;
- (int) test1: (int) a1;

//オーバーロード(int型の引数)
- (int) test1: (int)    a1 arg2: (int)    a2; 

//オーバーロード(double型の引数) 
- (int) test1: (double) a1 arg2: (double) a2; 
@end;

このようにすると、コンパイル時に以下のエラーが出力されました。

error: duplicate declaration of method ‘-test1:arg2:’

むむむ。どうやら型で区別はされていないみたい。C++/Javaの場合はこのように型が異なる場合のオーバーロードは可能ですが、Objective-Cではこれが出来ないようです。では、型ではなく引数のラベル名を変更してみます。以下サンプルのオーバーロードメソッド3、4、5が該当します。
メソッド3と4は引数の型は同じで、ラベルが異なるパターン。
メソッド4と5は引数の型も、ラベルも異なるパターンになります。

/*************/
/* class A ***/
/*************/
@interface A : NSObject {
}
- (int) test1;
- (int) test1: (int) a1;

//オーバーロード3(ラベルが「arg2」)
- (int) test1: (int) a1  arg2: (int) a2;   

//オーバーロード4(ラベルが「argg2」)
- (int) test1: (int) a1 argg2: (int) a2;

//オーバーロード5(ラベルが「arggg2」)
- (int) test1: (double) a1 arggg2: (double) a2;
   
@end;

@implementation A 
    // オーバーロードメソッド1
    - (int) test1 {
        printf("wow1.\n");
    }

    // オーバーロードメソッド2
    - (int) test1: (int) a1 {
        printf("wow2. arg1 = %d.\n", a1); 
    }

    // オーバーロードメソッド3
    - (int) test1: (int) a1 arg2: (int) a2 {
        printf("wow3. arg1 = %d. arg2 = %d\n", a1, a2);
    }

    // オーバーロードメソッド4
    - (int) test1: (int) a1 argg2: (int) a2 {
        printf("wow4. arg1 = %d. arg2 = %d\n", a1, a2);
    }

    //オーバーロードメソッド5
    - (int) test1: (double) a1 arggg2: (double) a2 {
        printf("wow5. arg1 = %f. arg2 = %f\n", a1, a2);
    }


@end

/**********
 * main
**********/
int main(void) {
    A* classA = [[A alloc] init];
    [classA test1];
    [classA test1: 1];
    [classA test1: 1   arg2: 2]; //オーバーロード
    [classA test1: 3  argg2: 4]; //オーバーロード
    [classA test1: 5 arggg2: 6]; //オーバーロード

    return 0;
}
この場合、コンパイル時にはエラーが出ませんでした、
実行結果

wow1.
wow2. arg1 = 1.
wow3. arg1 = 1. arg2 = 2
wow4. arg1 = 3. arg2 = 4
wow5. arg1 = 5.000000. arg2 = 6.000000

問題無く実行出来ています。結局の所、Objective-Cは型ではなく、メッセージセレクタ(ラベル)の違いによりオーバーロードが可能ということみたいです。Java/C++では引数の数、型で区別しているので、逆に同一の型の場合はオーバーロード出来ません。このあたり、使い方によっては便利(?)なのかもしれませんね。

セレクタのパラメータを省略したらどうなるんでしょ?

/**********
 * main
**********/
int main(void) {
    A* classA = [[A alloc] init];
    [classA test1];
    [classA test1: 1];
    [classA test1: 1 arg2: 2];  //オーバーロード
    [classA test1: 3 argg2: 4]; //オーバーロード
    [classA test1: 5 : 6];      //オーバーロード ←ココ

    return 0;
}

コンパイル時に以下のワーニングが出力され、

overload.m: In function ‘main’:
overload.m:49: warning: ‘A’ may not respond to ‘-test1::’
overload.m:49: warning: (Messages without a matching method signature
overload.m:49: warning: will be assumed to return ‘id’ and accept
overload.m:49: warning: ‘...’ as arguments.)

そのまま、無視して実行してみると、実行時例外で落ちました。

なんとなく分かりました。省略した場合はid型になるらしいので、そんなメソッドは無いって事ですね。きっと。そして、実行しても存在しないから落ちるってことだと思います。

0 件のコメント :

コメントを投稿