Edited at

オブジェクト指向ってなんやねんという自分のためのメモ

More than 1 year has passed since last update.

プログラミングを始めてはや1年。

一応Androidアプリを作ったこともあって全く触れてないわけじゃないけど、オブジェクト指向ってなんやねんと思い続けている。

さすがに理解しておかないと今後つらいのでメモを作成しておく。

JavaとRubyで書きます


ここに書かれているのはプログラミング超初心者の筆者が調べてまとめたものなので、間違った表記・意味がたくさんあると思います。

間違っているところはご指摘頂けると嬉しいです。


ちょっとだけクラスについて

Mainクラスにメソッドをめっちゃいっぱい作っちゃった!! これもうわかんねえな!


Main.java

class Main{

public static void main(String[] args){
hello();
sorry();
goodbye();
fine();
.
.
.
}
public static void hello(){
//処理文
}
public static void sorry(){
//処理文
}
.
.
.
}



他にもクラスを作ってたくさんメソッドを書こう!


Main.java

class Main{

public static void main(String[] args){
Person.hello();
}
}


Person.java

class Person{

public static void hello(){
System.out.println("Hello World");
}
}

(Rubyでは)


index.rb

class Person

#内容
end

すごーい!すっきりー!


オブジェクトってなに

現実世界の「モノ」を表している(ってよくいろんなとこに書いてある)

例えば、Otakuっていうオブジェクトがあるとしたら、

スペック(情報)

- 28歳

- 独身

- 彼女いない歴=年齢

- 童貞

- デブ

できること(振る舞い)

- PCを使える

- 部屋にひきこもれる

- アニメの話ができる

という感じで説明できる(偏見)


オブジェクトは別名、インスタンスという。


オブジェクト = インスタンス !

インスタンスを作るにはクラスが必要。

つまりクラスはインスタンスの設計図。(ってよくいろんなとこに書いてある)

それじゃあ otaku を定義しよう。簡単だ。

Otakuクラス(設計図)に、インスタンスの「スペック」「できること」を書けばいい。


Otakuを作ろう


Main.java

class Main{

public static void main(String[] args){
Otaku otaku1 = new Otaku(); //➀ Otakuクラスのインスタンスをつくるぞ! インスタンスは変数に代入して使うよ!
//クラス型 変数名(インスタンス名) = new クラス名();
Otaku otaku2 = new Otaku();
Otaku otaku3 = new Otaku(); //➁ 同じクラス型からたくさんインスタンスを作れるよ! otakuがたくさんいるね!

//➃ インスタンス名.メソッド名() でOtakuのできることが使えるよ!
otaku1.usePC();
otaku1.specBody(); //➄specBodyメソッドで「僕はデブです!」って言いたい! インスタンスフィールドbodyの値("デブ")を使いたい!
}
}



Otaku.java

class Otaku{

//➂ ここにはOtakuのスペック(情報)を定義するよ! 難しい言葉でインスタンスフィールドっていうらしい?けど変数宣言だ!(たぶん!)
public String age;
public String body;

//➂コンストラクタで値を代入!
Otaku(){
otaku1.age = "28歳";
otaku1.body = "デブ";
}

//➃ ここからOtakuのできること(振る舞い)をメソッドで表現するよ! インスタンスメソッドとも言うよ!
public void usePC(){
System.out.println("僕はパソコンの大先生なんだぞ!");
}
public void specBody(){
System.out.println("僕は" * this.body + "です!"); //➄ MainクラスでspecBodyメソッドが呼ばれた時に、thisをインスタンス名に置き換えるよ! これで「僕はデブです!」って言えるね!
}
}


(Rubyでのインスタンスメソッドの使い方)


otaku.rb

class Otaku

def usePC #インスタンスメソッドの定義
puts "僕はパソコンの大先生なんだぞ!"
end
end

otaku = Otaku.new #インスタンス生成
otaku.usePC



publicとかstaticとか

public ・・・ クラス外からでも呼び出せる

static ・・・ インスタンスを作っていなくても呼び出せる


コンストラクタについてちょっと詳しく

コンストラクタとは、newを使ってインスタンス生成をした後に自動で呼び出される特別なメソッドのこと。

Rubyでの「initializeメソッド」と同義

インスタンスフィールドの値を設定するときにコンストラクタを使うのと使わないのじゃ、結構コードが変わってくるよ!

ちゃんと覚えよう!

コンストラクタなしver


Main.java

class Main{

public static void main(String[] args){
Otaku otaku1 = new Otaku(); //➀ Otakuクラスのインスタンスをつくるぞ! インスタンスは変数に代入して使うよ!
//クラス型 変数名(インスタンス名) = new クラス名();
Otaku otaku2 = new Otaku();
Otaku otaku3 = new Otaku(); //➁ 同じクラス型からたくさんインスタンスを作れるよ! otakuがたくさんいるね!

otaku1.age = "28歳"; //➂各インスタンスにOtakuの細かい情報を入れていくよ! これじゃダメ人間だね!
otaku1.body = "デブ";

otaku1.usePC(); //➃ インスタンス名.メソッド名() でOtakuのできることが使えるよ!
otaku1.specBody(); //➄specBodyメソッドで「僕はデブです!」って言いたい! インスタンスフィールドbodyの値("デブ")を使いたい!
}
}


このコードの

otaku1.age = "28歳";

otaku1.body = "デブ";

のところで値を設定してるよ。

これだとインスタンスフィールドが増えてきたら大変だー! たくさんあってわかんないよー!

コンストラクタありver


Main.java

class Main{

public static void main(String[] args){
Otaku otaku1 = new Otaku(); //➀ Otakuクラスのインスタンスをつくるぞ! インスタンスは変数に代入して使うよ!
//クラス型 変数名(インスタンス名) = new クラス名();
Otaku otaku2 = new Otaku();
Otaku otaku3 = new Otaku(); //➁ 同じクラス型からたくさんインスタンスを作れるよ! otakuがたくさんいるね!

//ないぞ!

otaku1.usePC(); //➃ インスタンス名.メソッド名() でOtakuのできることが使えるよ!
otaku1.specBody(); //➄specBodyメソッドで「僕はデブです!」って言いたい! インスタンスフィールドbodyの値("デブ")を使いたい!
}
}



Otaku.java

class Otaku{

//➂ ここにはOtakuのスペック(情報)を定義するよ! 難しい言葉でインスタンスフィールドっていうらしい?けど変数宣言だ!(たぶん!)
public String age;
public String body;

//ここでコンストラクタを使うよ! new Otaku(); が呼ばれたときに必ず使われるメソッドだ!
Otaku(){
otaku1.age = "28歳"; //➂各インスタンスにOtakuの細かい情報を入れていくよ! これじゃダメ人間だね!
otaku1.body = "デブ";
}

//➃ ここからOtakuのできること(振る舞い)をメソッドで表現するよ! インスタンスメソッドとも言うよ!
public void usePC(){
System.out.println("僕はパソコンの大先生なんだぞ!");
}
public void specBody(){
System.out.println("僕は" * this.body + "です!"); //➄ MainクラスでspecBodyメソッドが呼ばれた時に、thisをインスタンス名に置き換えるよ! これで「僕はデブです!」って言えるね!
}
}



コンストラクタを書くときのルール!


  • コンストラクタ名はクラス名と同じにする

  • 返り値を書いてはいけない(voidもいらない)

また、インスタンス生成のときに引数で値を設定してコンストラクタに渡すこともできるよ!


Main.java

Otaku otaku = new Otaku("デブ");



Otaku.java

class Otaku{

public String body;
Otaku(String body){ //"デブ"が入る
this.body = body; //otaku.body = "デブ"; と同じ!
}
}

Rubyだとこうなる


index.rb

class Otaku

def initialize(name) //インスタンス生成時に行いたい処理
@name = name #インスタンス変数(インスタンスの持つ情報(データ))
end
def info
puts "私は
#{@name}です" #クラス内でのみインスタンス変数は利用可能
end
end

otaku = Otaku.new("デブ")
otaku.info



クラスフィールドとかクラスメソッドについて

そろそろ頭が追いついてないとこ。

クラスにも情報が持てるってことかな?

クラスにもメソッドが持てる。そもそも

public static void main(String[] args){

}

これもクラスメソッドだよね。


Otaku.java

class Otaku{

static int count = 0; //クラス変数(全てのインスタンスで使える変数)

//インスタンスフィールド
public String age;
public String body;

//コンストラクタ
Otaku(){
Otaku.count++; //クラスフィールドにアクセス
}
}


Rubyでのクラスメソッドの書き方


index.rb

class Otaku

@@count = 0 #クラス変数の定義

def initialize(name)
@name = name
@@count += 1
end
def Otaku.data #クラスメソッドの定義 self.クラスメソッドとしても可
puts "nameです"
end
end

Otaku.data #クラスメソッドの呼び出し
#出力結果 nameです



カプセル化って...?

いやーきついっす

簡単に言うと、作った機能の内の安全な部分を公開して、使ってほしくない部分を制限(カプセル化)すること。

フィールドとメソッドへのアクセスを制限することでできる。

ワイ「クラス作ったやで!見てこれ!すごいじゃろ!他のクラスでも使ったええで!」

「public」をつける!


Main.java

class Main(){

public static void main(String[] args){
Otaku otaku = new Otaku("wai"); //インスタンス生成
System.out.println(otaku.name); //publicに公開してるから使えるで!
}
}


Otaku.java

class Otaku{

//インスタンスフィールド
public String name; //クラスの外部からアクセスできるように「public」をつける!

//コンストラクタ
Otaku(String name){
this.name = name;
}
}


ワイ「クラス作ったやで!見てこれ!でも他のクラスで使ったらバグ起こしそうだから使わせられんわ...」

「private」をつける!


Main.java

class Main(){

public static void main(String[] args){
Otaku otaku = new Otaku(28); //インスタンス生成
System.out.println(otaku.age); //エラー!!! 使えません!
}


Otaku.java

class Otaku{

//インスタンスフィールド
private int age; //外部からアクセスできないようにするために「private」をつける!

//コンストラクタ
Otaku(int age){
this.age = age; //同じクラス内なら使えるよ!
}
}


Rubyのメソッドは何も指定しない限りはpublicメソッドとなる。

privateの後に書いたメソッドはprivateメソッドとなり、クラス内でしか呼び出せなくなる

しかーし、

インスタンスフィールドをprivateにしても安全に値を頂きたいお客様のために、フィールドの返り値のみをご利用できる「ゲッター」というメソッドをご提供!


Main.java

class Main{

public static void main(String[] args){
Otaku otaku = new Otaku("~","~","~");
System.out.println(otaku.getName());
}
}


Otaku.java

class Otaku{

//インスタンスフィールド
~
private String name; //ここは「private」だけど、、、
~

//インスタンスメソッド
public String getName(){ //ここが「public」なので、クラス外からアクセス可能
return this.name; //フィールドの値を返す
}
}


Rubyでもインスタンス変数の値はクラス内でしか取得できないので、ゲッターを使ってクラス外から取得できる


index.rb

class Otaku

attr_reader:name #ゲッター定義
def initialize(name)
@name = name
end
end

otaku = Otaku.new("ブス")
puts otaku.name


attr_readerでは↓と同義のことを行ってゲッターを定義している

def name

@name #戻り値(returnは省略)
end

反対に、外部からフィールドの値を変更するメソッド「セッター」もあります


Main.java

class Main{

public static void main(String[] args){
Otaku otaku = new Otaku("~","~","~");
otaku.setName("washi"); //setNameメソッドに引数セット
System.out.println(otaku.getName());
//出力結果: washi
}
}


Otaku.java

class Otaku{

~
private String name;
~

public void setName(String name){
this.name = name; //フィールドに値をセット
}
}


Rubyでのセッター定義


index.rb

class Otaku

attr_writer:name #セッターの定義

def initialize(name)
@name = name
end
end

otaku = Otaku.new("ブス")

otaku.name = "かわいい" #セッターの呼び出し
puts otaku.name #出力結果 かわいい


attr_writerでは↓と同義のことを行ってセッターを定義している

def name=(value)

@name = value
end

Rubyでゲッターとセッターを同時に定義する時に「attr_accessor」を用いると両方を一度に定義できる


基本的には、


  • フィールドはprivate

  • メソッドはpublic

にすることで、変数はprivateに守られ、かつ、メソッドは他のクラスでもpublicに使えるので便利。


継承!?

なんかかっこいい。

同じような内容のコードってまとめたくなるよね。それはクラスの内容でも同じこと。

あるクラスの内容を別のクラスが「引き継ぐ」ことを継承っていう。

先輩「俺の特技は汎用性ヤバイぜ!超使える!」 (継承される特技(クラス):スーパークラス)


Super.java

class Super{

public void setName(String name){
System.out.println(name);
}
}

後輩「先輩!それ使わせて!」 (継承してできる特技(クラス):サブクラス)


Sub.java

class Sub extends Super{  //Superクラスのインスタンスメソッドを継承している

}



Main.java

class Main{

public static void main(String[] args){
Sub sub = new Sub();
sub.setName("ハイパーキネティックポジションリバーサー!!!"); //スーパークラスのインスタンスメソッドを呼び出す
}
}

Rubyではこうなる


index.rb

class Super

def initialize(name)
@name = name
end
def info
puts @name
end
end

class Sub < Super
end

sub = Sub.new("ハイパーキネティックポジションリバーサー!!!") #newをすると親クラスのinitializeメソッドが実行される
sub.info #親クラスから継承しているのでinfoメソッドが使える


スーパークラスから継承したメソッドと同名のメソッドをサブクラスに定義すると、スーパークラスの内容を上書きすることができる。

これを「オーバーライド」という。

また、「private」がそのクラス内からのみアクセス可能だったのに対して、「protected」はそのクラス内とサブクラスからのみアクセス可能になる。


気づいたこと・注意点


  • メソッドはクラスの中に定義する

  • thisはクラス内のメソッドやインスタンスを指すときに使用する

  • 変数の定義に値を代入しなかった場合、String型には「null」、int型には「0」、double型には「0.0」、boolean型には「false」が入る

  • サブクラスのインスタンスはスーパークラスのメソッドも呼べるが、その逆はできない。

  • クラス名のネタををオタクにしたのは完全に深夜テンションのせいです。

  • ハイパーキネティックポジションリバーサーはLoLっていうゲームからとってきただけなので特に意味は無いです()