クラスは知ってるけれどクラスの使い道がわからないあなたへ

「クラスは知ってるけどクラスの使い道がわからない」
「クラスとインスタンスの違いがよくわからない」

初心者にありがちなことだと思います。
クラスの存在価値がわかるまでって意外と大変です。

クラス・インスタンスオブジェクト指向といえばJavaな気がします。
今日はクラスのありがたみをJavaのサンプルコードを用いながら、
簡単にではありますが説明してみたいと思います。

ここではクラスの「使い方」ではなくて「使い道」について話していきます。
(使い方がわからなくても、ついていけるとは思います)


それではまずは、簡単な問題を出してみたいと思います。
怖がらないでください。

(1)あるグラフの上に、図のようなに3つの点が有ります。この3つの点の座標を持つプログラムを書いてみてください。

特に強いこだわりがなければ、ファイル名はLesson.javaとつけることにしましょう。

f:id:cocodrips:20150214002429p:plain




かけましたか?
書けたら、次の問題をみてみましょう。

(2) 点1と、点2の距離をプリントするプログラムを書いてください。


追記(2/14)
自分がユークリッド距離ではなくマンハッタン距離を求めるサンプルコードを書いてたので、
マンハッタン距離を求めてください( ˘•ω•˘ ) すみません。
マンハッタン距離はこのへんを見るとわかると思います:
Tech Tips: いろいろな距離概念






どうですか? そんなに難しくなかったでしょう?


それではこの問題のサンプルコードを見ていきます。

【初心者レベル1】サンプルプログラム

とっても単純に書いた例を見てみましょう。

public class Lesson {
    public static void main(String[] args) {
        int x1 = 1;
        int y1 = 1;
        
        int x2 = 2;
        int y2 = 5;
        
        int x3 = 5;
        int y3 = 4;
        
        System.out.println(Math.abs(x1 - x2) + Math.abs(y1 - y2));

    }
}

このプログラムを見て笑った人は、きっと「配列」の存在を知っているのでしょう。
配列を使ったプログラムも見てみましょう。

【初心者レベル2】サンプルプログラム

public class Lesson {
    public static void main(String[] args) {
        int points[][] = new int[3][3];
        points[0][0] = 1;
        points[0][1] = 1;

        points[1][0] = 2;
        points[1][1] = 5;

        points[2][0] = 5;
        points[2][1] = 4;

        System.out.println(Math.abs(points[0][0] - points[1][0]) + Math.abs(points[0][1] - points[1][1]));

    }
}

こんな感じにになりましたか?
さてさて、宣言する変数の数は減りましたが、なんだか何をしているのかわからないコードになりましたね。

このコードを1ヶ月後にあなた、もしくは他の人が見たとします。

System.out.println(Math.abs(points[0][0] - points[1][0]) + Math.abs(points[0][1] - points[1][1]));

points[0][0]ってなんでしょうか?

0番目のポイントの・・・0番目?
これだけ見てもわからなくなくなってしまう可能性があります。


ここで登場するのがクラスです。
points[0][0]が、points[0].x って書けたら、
これは0番目のポイントの、x座標かな?って見た瞬間に思えます。

これから、座標を保持するPointクラスを作ってみます。

座標を保持するPointクラスを作ってみる

さっきと別ファイルを作って、Point.javaと名前をつけましょう。

public class Point {
    public int x;
    public int y;
}

これで完成です! xとyという値を持つPointクラスを作りました。
早速使ってみましょう!
配列をつかったコードを、Pointクラスを使ったコードに書き換えてみます。
自分で書けるかも?って思った人は、サンプルコードを見る前に、自分でかいてみてください。


クラスを使ったサンプルコード

作ったPointクラスの使い方はこんなかんじでしたね。

Point point = new Point();


サンプルコードはこうなります。

public class Lesson {
    public static void main(String[] args) {
        Point points[] = new Point[3];
        
        points[0] = new Point();
        points[0].x = 1;
        points[0].y = 1;

        points[1] = new Point();
        points[1].x = 2;
        points[1].y = 5;

        points[2] = new Point();
        points[2].x = 5;
        points[2].y = 4;

        System.out.println( Math.abs(points[0].x - points[1].x) + Math.abs(points[0].y - points[1].y));
    }
}

xとyがあって、座標を管理してるっぽい感じになってきました。
でもコード量が一番はじめよりだいぶ増えてしまいましたね・・・。

でもまだくじけないでください。
今からもっとこのサンプルコードはさっぱりします。

コンストラクタって知ってますか?

クラスを知っている人なら、聞いたことが有るんじゃないでしょうか。
コンストラクタは、「クラスの生成時に呼ばれる特別な関数」みたいなものです。
Javaでは、クラス名と同じ名前の関数がコンストラクタとなり、new を書いた時に呼ばれます。

先ほどサンプルコードで、こんなふうに書きましたよね。

points[0] = new Point();
points[0].x = 1;
points[0].y = 1;

でも、わざわざnew Point()ってして、xとyに入れて・・・
めんどくさくないですか?
コンストラクタを使うと、実はこれ、次のサンプルみたいに書けるんです。

points[0] = new Point(1, 1);

3行から1行に減りました!

でもこう書いただけじゃまだ動きません。
コンストラクタの関数をかいてないですからね。

コンストラクタをPoint.javaに追加してみましょう。

public class Point {
    public int x;
    public int y;

    // ここを追加
    Point(int x, int y) {
        this.x = x;
        this.y = y; 
    }
}

Pointクラスのコンストラクタは、x, yを受け取り、Pointクラスのオブジェクトから、
.x や .y でアクセスできるところに代入します。

さて、このコンストラクタを追加したら、
先ほどのサンプルコードを、コンストラクタを使った形に書き換えてみましょう。

コンストラクタを使ってポイントクラスのオブジェクトを作る

サンプルコードがこんなかんじになります。

public class Lesson {
    public static void main(String[] args) {
        Point points[] = new Point[3];

        points[0] = new Point(1, 1);
        points[1] = new Point(2, 5);
        points[2] = new Point(5, 4);

        System.out.println(Math.abs(points[0].x - points[1].x) + Math.abs(points[0].y - points[1].y));
    }
}

行数が!減りました!
やったー!


クラスって怖くなくないですか?
最初は配列だった値に、名前をつけてあげただけです。


ここまで来たら、あと少しです!
次は、クラスに関数を追加してあげましょう。

Pointクラスに関数を追加してみよう

今まで、サンプルコードでは1番目の点と、2番目の点の距離をprintしてきました。
でも、もしこのあと、2番目と3番目の距離、
3番目と1番目の距離もプリントするとしたら・・・?
このままだと、こんなふうに書くことになります。

public class Lesson {
    public static void main(String[] args) {
        Point points[] = new Point[3];

        points[0] = new Point(1, 1);
        points[1] = new Point(2, 5);
        points[2] = new Point(5, 4);

        System.out.println(Math.abs(points[0].x - points[1].x) + Math.abs(points[0].y - points[1].y));
        System.out.println(Math.abs(points[1].x - points[2].x) + Math.abs(points[1].y - points[2].y));
        System.out.println(Math.abs(points[2].x - points[0].x) + Math.abs(points[2].y - points[0].y));
    }
}


System.out.println(Math.abs(points[0].x - points[1].x) + Math.abs(points[0].y - points[1].y));
この1行は、ほとんど同じなのに、何度も書いていてめんどくさいですね・・・。

おんなじことを書くのはプログラマーにとって悪なのです!
こんなのは、関数にしてしまいましょう!

Point.javaを開いて、自分自身の座標と、もう一つの座標の距離を測る関数を追加します。

public class Point {
    public int x;
    public int y;

    Point(int x, int y) {
        this.x = x;
        this.y = y; 
    }
    
    // ここを追加
    int distance(Point other) {
        return Math.abs(this.x - other.x) + Math.abs(this.y - other.y);
    }
}


ちょっと難しくなってきましたか?
でも、この関数を加えると、Pointクラスは、もう一つのPointクラスのオブジェクトを受け取ると、
自分の座標と、もう一つの座標の距離を返してくれる機能を持ったのです!
どうやって使うのでしょうか?

先ほど以下のように書いていた部分ですが・・・・

Math.abs(points[0].x - points[1].x) + Math.abs(points[0].y - points[1].y)

こんな風に書けるようになりました。

points[0].distance(points[1])

ちょっとよくこの文字の並びを見てみましょう。むしろ声に出して読みましょう。
points[0].distance(points[1])

「points[0] でぃすたんす points[1]」
どうみても、どう聞いてもこれはpoints[0]とpoints[1]の距離だってことがわかるようになりました。
これなら1ヶ月後の自分がみても、何をしていたか理解できそうです!よね?

最初と最後のサンプルコードを比べてみる

比べてみましょう!
最初に配列を使った時は、こんなかんじでしたね。

Lesson.java

public class Lesson {
    public static void main(String[] args) {
        int points[][] = new int[3][3];
        points[0][0] = 1;
        points[0][1] = 1;

        points[1][0] = 2;
        points[1][1] = 5;

        points[2][0] = 5;
        points[2][1] = 4;

        System.out.println(Math.abs(points[0][0] - points[1][0]) + Math.abs(points[0][1] - points[1][1]));

    }
}


それが、こんなふうに2つのファイルになりました!

Lesson.java

public class Lesson {
    public static void main(String[] args) {
        Point points[] = new Point[3];

        points[0] = new Point(1, 1);
        points[1] = new Point(2, 5);
        points[2] = new Point(5, 4);

        System.out.println(points[0].distance(points[1]));
    }
}


Point.java

public class Point {
    public int x;
    public int y;

    Point(int x, int y) {
        this.x = x;
        this.y = y; 
    }
    
    int distance(Point other) {
        return Math.abs(this.x - other.x) + Math.abs(this.y - other.y);
    }
}

全部まとめると、コードの「行数」は減っていません。

でも最後のサンプルは、最初のサンプルより、「未来の自分」や「他人」が読んでも、わかりやすいコードになりました。

クラスって何が嬉しいの?

クラスの良い所は、たくさんあります。きっと調べたらいっぱいでてきます。
でもまずは、point[0]よりも、point.xのほうが、
何を指しているのかわかりやすいということを感じてほしいなと私は思います。

最後に

ずいぶん長くなりました。初めてこんな長いブログを書いた気がします。
「クラスの使い道」は、自分がプログラムを学んで一番わからなかったところです。
学校の後輩にも、同じように感じている子が多いことがわかったので、
こんな記事を書いてみました。

私はJavaは初心者なので、玄人の方からみたら変な部分もあるかと思います。
わからない部分・おかしな部分等あればぜひコメントをください。