JAVAを勉強している者です。説明が下手ですいませんが、オブジェクト指向の「抽象」及び「抽象クラス」って一体何なんでしょうか?
ネットや参考書みても「あいまいな及びあいまいなクラス」と書いてあるんですが、何なのかよくわかりません。
まだ勉強不足だと思いますが、「抽象」というのは、「プログラムの処理内容がまだ確定(未完成?)していない、詳細未定(未完成?)メソッド」みたいなことが書かれてありました。
さらに、「抽象クラス」というのは「フィールドと詳細未定(未完成?)メソッドが存在するクラス」みたいなことが書かれてあり、その「詳細未定(未完成?)メソッドがたくさん存在するほど抽象度(あいまい度)が高いクラス」という風に書かれていました。(説明が下手ですいません。)
そもそも「プログラムの処理内容がまだ確定(未完成?)していない、詳細未定(未完成?)」というのは、しっかりと設計していないことであり、きちんと完璧に設計すればいいと思うのですが、なぜそんなことがあり得るんですか?
それに「プログラムの処理内容がまだ確定(未完成?)していない、詳細未定(未完成?)」は多人数で開発していると起こりやすいと思いますが、一人での開発ではそんなことは起きないのでは?と思っております。
わかりやすいような例や例えで教えてくれませんか?
できれば身近な例を挙げて教えてくれませんか?
よろしくお願いします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/01/11 06:33 編集

回答12件
0
抽象とは?
抽象は具体の反対です。
例として一つ、りんごの木を思い浮かべてみましょう。
その思い浮かべたりんごの木は、中央に太い幹があり、上に丸くてふさふさな緑があり、枝が2~3本生えててりんごがぶら下がっては居ませんか?
それってちゃんと具体的にイメージ出来てますか?
幹だって日光や水、肥料次第で千差万別で同じ物は一つもありません。
枝葉の細部なんて1本たりとも同じ物は存在しません。
そして何より、今は冬なんでりんごがぶら下がっているわけがありません。
ですが、私達は子供にりんごの木を説明する時に、
デフォルメ化された上記のりんごの木を描いて説明します。
りんごに関して詳しい農家や研究科同士が具体的なものについて議論する場合は最初から具体的で良いでしょうが、
私達のような畑違いの人が普段の会話に用いられるものはまずデフォルメされた抽象的なものを使ってわかりやすく説明していきます。
その後、会話の流れや必要に応じて細部に着目していく具体化が行われます。
プログラミングの世界での抽象とは?
内部構造は違うが見た目や目的が同じ部品を使い分ける時に威力を発揮します。
例えばA街からB街へ荷物を運ぶという仕事があったとしましょう。
以前は技術が発達していなかったので、手段として馬車を使いました。
時間が経ち、エンジンで駆動するトラックが発明されました。
A街からB街へ荷物を運んで欲しいAさんには知ったことではありません。
馬車の行者インスタンスの集荷メソッドを叩き、引数に「物・住所・賃金」を渡す箇所が、
トラックの運転手インスタンスの集荷メソッドを叩き、引数に「物と住所と賃金」を渡すように修正されるだけの話です。
そこで「A街からB街へものを運ぶ仕事の人」という共通点をまとめた抽象クラスを用意します。
馬と車は構造が違うので同じロジックは使えないので、抽象クラスでは「集荷メソッド」とその引数だけを定義しておきます。
これが設計書になり、後から馬車やトラックを実装する人は「ああ、集荷というメソッドを用意すればいいんだな」と何を作ればよいかが明確になります。
近い将来、ドローンという空飛ぶ飛行機を操作する人が登場しても、そのモジュールを使うのに既存ロジックを作り直す必要はなく、テストも簡単に通す事が出来ますね。
また、優れたエンジニアが後からジョインした場合、抽象クラスとその実装をちら見することでどういう目的で作られたのかを把握しやすくなります。
まさに会話のテクニックで使われる森と木ですね!
実際に使われるケース
よく用いられるのはデータベースですかね?
データを読み書きしたいクラスはただただ持っているデータベースインスタンスのquery
を叩いてSQL文を送れば良い仕組みを作っておき、
MySQLに接続するモジュール、Oracleに接続するモジュール…という風にあDBの操作をまとめた抽象クラスを定義しておくと、個々のDBの実装に影響されにくいシステムを構築出来ます。
投稿2017/01/08 06:08
総合スコア21384
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
抽象クラスがどんなときに必要になるのか、どういう風に役に立つのか、そんなことを示すためのサンプルプログラムを作りましたので、これを用いて説明していきます。
サンプルプログラム
Gist: 抽象クラスを説明するためのRPGプログラム
さて、何かちょっとしたRPGを作ることになりました。でも、本格的なのは面倒なので「戦闘」部分だけ作ることになりました。戦闘はもちろん自動です。ユーザーに操作させるようなゲームは時代遅れですから。
ということで、早速外側から見て行きましょう。RPGクラスが大枠のメインです。BattleFieldクラスは戦闘の状態というか戦闘の場所というか、そういった感じで戦闘を管理するものです。実際の戦闘を行わせる場合、オブジェクトには最低限必要な項目があります。例えば、名前が無ければメッセージが出せませんので、名前を取得するメソッド(getName()
)、生きているかどうかわからなければいけませんので、生存確認するメソッド(isAlive()
)などが必要です。これがちゃんと実装されていれば、問題なく戦闘が行えるはずですので、それらがあることを示すためにBattlableインターフェースを作ります。
ここで重要なのはBattleFieldクラスにとってBattlableインターフェースさえあれば十分と言うことです。それぞれのメソッドがどのように実装されているのかは関係ありません。Battlableインターフェースさえ実装されていることが保証されていれば、問題なく戦闘できるでしょう。
ここまではいいですね。では、次に、実際のプレイヤーやモンスターの実装に入ります。人間(Human)にはレベルという概念があるシステムを採用しようと思います。レベルに応じて攻撃力や防御力が高くなると言うことです。対して、モンスター(Monster)にはレベルという概念が無く、攻撃力や防御力そのものを設定するとシステムを採用しようと思います。他にも人間には命を削って発動する超必殺技みたいなのもできるようにようにしたいと考えています。となると、HumanクラスとMonsterクラス、それぞれ別に作る必要があります。あとはBattlableインターフェースを両方とも実装していれば問題ないですね。
**ちょっと待ってください。**攻撃力関係は確かに違いますが、HPに関する考えは一緒です。どちらも最大HPが最初のHPで、ダメージによって減っていって、0になれば戦闘不能になります。このままHumanとMonsnterを別々に作ってしまったら、このHP処理が二つの箇所でそれぞれ書く必要が出てきてしまいます。これはちょっと面倒です。
ということで、なら、共通部分を書いたものを用意しよう…となって出てくるの抽象クラスであるAbstractCreatureクラスです。AbstractCreatureクラスでは、Battlableインターフェースを含んでおいて、両方に共通になる部分を実装してしまいます。ただ、攻撃力や防御力というものは、具体的にどんなものなのかが決まらないと規定できず、実装できません。そう、どうしても未確定な部分が出てきます。そのため、そのまま直接インスタンスにはできない抽象クラスとして定義する必要があります。
このAbstractCreatureクラスを継承してHumanクラスやMonsterクラスを作るととても便利だとわかります。HumanクラスやMonsterクラスで面倒なHP計算とかの処理が不要になるからです。このように、コードを共通化するために親クラスを用意することはよくあるパターンです。ですが、その親クラスで未確定な部分が出てしまう場合に、抽象クラスにすることで、未確定な部分、つまり、抽象メソッドをサブクラスでの実装に任せると言うことができます。
もう一つ重要なことがあります。**抽象クラスを継承するサブクラス自体が抽象クラスで無ければ、そのクラスで抽象メソッドは必ず実装しなければなりません。**これは、インターフェースと一緒です。このように、実装を強制し、実装漏れを防ぐと言うこともできます。
だいたいわかったでしょうか?実装の強制という所ではインターフェースと同じです。しかし、抽象クラスでは、他のクラスのようにフィールドや実装されたメソッドが使用できます。つまり、コードの共通部分をまとめて親クラスに書いておきたい。しかし、その親クラスには未確定部分が含まれるため、普通のクラスにできない。そんなときに使うのが抽象クラスとなります。
なお、Java8からはインターフェースでdefaultメソッドというものが定義できるようになりました。これは、実装を含むメソッドであり、インタフェースを継承する全てで使える共通のコードを書くことができます。つまり、今まで抽象クラスが担っていた、コードの共通化をインターフェースもできるようになりました。ただ、インタフェースはコンストラクタやインスタンスフィールドを持つことはできませんので、抽象クラスの機能全てがインタフェースでまかなえるようになったというわけではありません。
投稿2017/01/08 07:10
総合スコア21741
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
確かに「あいまいなクラス」という言い方をしてはわかりにくいですね。
しかし、あいまいということは裏を返せばいろいろな形をとりえるということでもあります。
抽象クラスではなくてインタフェースですが、Listを例にとって説明したいと思います。
Listの実装型として、
- 内部を配列として持ち、状況に応じて配列のサイズを適宜変更できるようにしたArrayList
- それぞれの要素が、リストの中で自分の次および前の要素が何かを持っているLinkedList
などがあります。後者がリストとしてイメージしづらいかもしれませんが、{1,2,3,4,5}のようなリストがあったとして、「3」という要素が、自分の前が「2」、自分の後が「4」という情報を持っており、そういう情報をすべての要素が持っていることでリストとしての体裁を持っています。何のためにと思うかもしれませんが、こうすることで頻繁に要素を追加・削除する際に有利なのです。
ひとまず、この2つを例に挙げて説明します。
例えば、リスト内のある場所、例えば「3番目」の要素を取得したいとします。
ArrayListの場合はリストを配列で持っているため、**配列にアクセスするのと同じようにarray[3]のようにして取得できます。**しかしLinkedListの場合、要素の位置に番号が振られているわけではないので、配列のようにアクセスできません。じゃあどうするのかというと、先頭の要素から順番に数えながら「3番目」を探します。
このように、同じ「3番目の要素を取得する」という操作でも、内部では全く別の処理をしているため、これを完全に別クラスとして記述すると「別のもの」として扱われ、統一された手法で操作できません。
もう一つ例を挙げましょう。リストの途中に要素を挿入したいとします。例えば要素が5つ(0番~4番)あるリストに対し、「2番目に要素を挿入したい」というような場合です。
ArrayListの場合、まず2番目の場所を開ける必要があります。そのため、2番目以降の要素を1個ずつ後ろに移動させ、そのあとで要素を2番目に収めるという操作になります。
一方LinkedListの場合、「1番目の要素の次」と「2番目の要素の前」を挿入したい要素に設定し直し、挿入する要素の前後をそれぞれ(元の)1番目と2番目に設定するという形になります。
これも、リストとしてやりたいことは一緒なのに、内部の構造が違うために全く違う操作になるため、全くの別メソッドになります。この2つを統一した方法で操作できないのは直感的ではないですよね?
そこで登場するのがListインタフェースです。
先ほど挙げた2例、つまり「番号を指定した要素の取得」と「番号と要素を指定した挿入」は、Listインタフェースでそれぞれget(int),add(int,T)(※Tはリストが扱う要素の型)という、質問者の言葉を使えばあいまいなメソッドを持っています。しかし、あいまいであるが故、どのような行動でも可能なのです。
List型の変数には、ArrayListもLinkedListも代入することができます。そして、どちらが入っていようとも、要素を取得したければget(int)、要素を挿入したければadd(int,T)と、統一された方法でリストを操作することができるのです。これが、抽象クラスやインタフェースのメリットです。
投稿2017/01/08 02:13
総合スコア20675
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
抽象クラスがある理由
抽象クラスが存在するのは「メソッドがあることを保証する」ことです。
保証というのは。。。
例えば、以下の抽象クラスはclose()
できることを保証するのが目的です。
Closeable
: close()できることを保証するクラス(自作クラスです)
java
1abstract class Closeable{ // (標準のクラスではなく自作です) 2 abstract void close(); 3}
例えば、これを継承して
MyFile
クラス
MySocket
クラスを作ります
(ファイルもソケットも両方ともclose()できることが保証できそうなものです)
(ソケットは通信で使うソケットを想定した名前です)
java
1 2class MyFile extends Closeable { // MyFileはclose()できることを保証します 3 String filePath; 4 .... 5 6 void close(){ 7 /** close()を実装しないとコンパイルエラーしちゃうので **/ 8 /** MyFileには、close()が呼び出せることが保証されます **/ 9 } 10} 11
java
1 2class MySocket extends Closeable { // MySocketはclose()できることを保証します 3 long id; 4 ... 5 6 void close(){ 7 /** MySocketには、close()が呼び出せることが保証されます **/ 8 } 9} 10
「インターフェースでもできるんじゃないの?」と思った方は下を読んでみてください。
インターフェースでもできるんじゃないの?
上のCloseable
はインターフェースでも実現可能です。
ただ、インターフェースだと実装が書けない[^1]ので、実装もしたくなったときに抽象クラスが活躍します。
「比較可能なことを保証するクラス」を抽象クラスを使って作ってみます。
Orderable
: 比較可能であることを保証するクラス(自作クラスです)
java
1 2abstract class Orderable{ 3 4 5 // [戻り値] 6 // this == ordなら 0 7 // this < ordなら 負の数 8 // this > ordなら 正の数 9 abstract int compare(Orderable ord); 10 11 12 // lessThan() 13 // graterThan() 14 // isTheSame() 15 // はcompareを使って実装できます 16 17 boolean lessThan(Orderable ord){ 18 return compare(ord) < 0; 19 } 20 21 boolean graterThan(Orderable ord){ 22 return compare(ord) > 0; 23 } 24 25 boolean isTheSame(Orderable ord){ // [^2] 26 return compare(ord) == 0; 27 } 28}
このOrderable
があれば、Javaプログラマはcompare
を実装するだけで、
lessThan()
もgraterThan()
もisTheSame()
も使えるようになります!一つ実装するだけで、他のメソッドも使えるようになるのは便利ですよね。
Orderable
の設計が問題があると思う方は、以下を御覧ください。
[念のために] サンプルコード妥協点
念のために書いておきます。
このOrderable
は設計に問題があります。
しかし、Orderable
の例は「一つ実装するだけで、他のメソッドも使えるようになるのは便利ですよね。」を納得してもらう目的で作りました。抽象クラスの説明に他のJavaの技術が混じって複雑にならないようにすることを選びました。
設計の問題というのは、
クラスA
、クラスB
も両方共Orderable
のcompare
をちゃんと定義したとします。
java
1class A extends Orderable{...} 2class B exteds Orderable{...}
そのとき
a
とb
という違うクラス同士での比較ができてしまう点が問題です。
この問題を解決するには、Orderable
のようなクラスを作るときはジェネリックスを使えば大丈夫です。
java
1A a = new A(); 2B b = new B(); 3a.lessThan(b); // これが問題
改良版Orderable
ジェネリックスを使って上記の問題を解決したOrderableです
java
1abstract class Orderable<T>{ 2 3 4 // [戻り値] 5 // this == ordなら 0 6 // this < ordなら 負の数 7 // this > ordなら 正の数 8 abstract int compare(T ord); 9 10 11 // lessThan 12 // graterThan 13 // isTheSame 14 // はcompareを使って実装できます 15 16 boolean lessThan(T ord){ 17 return compare(ord) < 0; 18 } 19 20 boolean graterThan(T ord){ 21 return compare(ord) > 0; 22 } 23 24 boolean isTheSame(T ord){ // [ここは気にしないで] equalsにしなかったのは、Object#equals(Object)を正しく実装できないからです 25 return compare(ord) == 0; 26 } 27} 28 29class MyInteger extends Orderable<MyInteger>{ 30 int value; 31 public MyInteger(int value){ 32 this.value = value; 33 } 34 35 36 int compare(MyInteger that){ // 注意:引数の名前はthatです 37 // this == thatなら0、this < thatなら負の数、this > thatなら正の数にちゃんとなります 38 return this.value - that.value; 39 } 40 41} 42
使い方は
java
1class MyInteger extends Orderable<MyInteger>{ 2 int value; 3 public MyInteger(int value){ 4 this.value = value; 5 } 6 7 8 int compare(MyInteger that){ // 注意:引数の名前はthatです 9 // this == thatなら0、this < thatなら負の数、this > thatなら正の数にちゃんとなります 10 return this.value - that.value; 11 } 12 13} 14 15
java
1MyInteger i1 = new MyInteger(10); 2MyInteger i2 = new MyInteger(5); 3 4System.out.println(i1.lessThan(i2)); // => false 5System.out.println(i1.graterThan(i2)); // => true 6System.out.println(i1.isTheSame(i2)); // => false 7
[^1]: Java 8以降からはdefault
を使ってインターフェースでも実装が書けるようになりました。しかし、Javaでは長い間、インターフェイスに実装が書けなかったのです。
[^2] equalsにしなかったのは、Object#equals(Object)
を正しく実装できないと判断したからです。
投稿2017/01/08 05:27
編集2017/01/08 05:33総合スコア145
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
抽象クラスは、それそのものだけでなく多態性(ポリモルフィズム)を使用した時に真価が発揮されるように思えます
以下が具体的にどう役に立つのかのコード例です
抽象クラスの概念がある事で、実装クラスの中身が実装クラスごとに変化しても一括してそれらを扱えるようにできる事が分かると思います
身近な例を用いて説明しました
ファイル名:Jyuyou.java
java
1abstract class 死 {// 抽象クラス 2 3 int i = 0; 4 5 abstract void 否認();// 自分の死そのものを疑う段階 6 7 abstract void 怒り();// 自分が死ぬことへの怒りを感じている段階 8 9 abstract void 取り引き();// 死を免れようとあがき回る段階 10 11 abstract void 抑うつ();// 気分が落ち込み何もできない段階 12 13 abstract void 受容();// 死をついに受け入れた段階 14 15 void 死の受容過程() {// 上記過程の連続再生 16 17 System.out.println("死の受容例" + (i + 1) + "人目"); 18 slp(); 19 System.out.println("\n---" + (i + 1) + "人目:" + "死の受容の過程1~否認~---"); 20 slp(); 21 否認(); 22 slp(); 23 System.out.println("\n---" + (i + 1) + "人目:" + "死の受容の過程2~怒り~---"); 24 slp(); 25 怒り(); 26 slp(); 27 System.out.println("\n---" + (i + 1) + "人目:" + "死の受容の過程3~取り引き~---"); 28 slp(); 29 取り引き(); 30 slp(); 31 System.out.println("\n---" + (i + 1) + "人目:" + "死の受容の過程4~抑うつ~---"); 32 slp(); 33 抑うつ(); 34 slp(); 35 System.out.println("\n---" + (i + 1) + "人目:" + "死の受容の過程5~受容~---"); 36 slp(); 37 受容(); 38 slp(); 39 System.out.println("\n\n\n\n"); 40 slp(); 41 42 } 43 44 void set(int o) {// 何人目か 45 i = o; 46 } 47 48 void slp() {// 表示間隔 49 try { 50 Thread.sleep(600); 51 } catch (Exception e) { 52 } 53 } 54 55} 56 57class 死を待つ人1 extends 死 {// 抽象クラスを実装したクラス1 58 59 void 否認() { 60 System.out.println("私は癌ではありません 検査のやり直しを要求します"); 61 } 62 63 void 怒り() { 64 System.out.println("何で俺が死ななきゃならないんだ! 死にたくないって言ってるだろ!"); 65 } 66 67 void 取り引き() { 68 System.out.println("すみません 癌の特効薬があると言うのはこちらですか"); 69 } 70 71 void 抑うつ() { 72 System.out.println("死にたくありません 僕は死にたくないんです"); 73 } 74 75 void 受容() { 76 System.out.println("最近、死ぬのも悪くないって思えるようになったんです"); 77 } 78 79} 80 81class 死を待つ人2 extends 死 {// 抽象クラスを実装したクラス2 82 83 void 否認() { 84 System.out.println("あの医者俺が死ぬなんて嘘なんでついたんだろ"); 85 } 86 87 void 怒り() { 88 System.out.println("やめろ・・・死ぬとかそんなの本人に言っていいと思ってるのか!"); 89 } 90 91 void 取り引き() { 92 System.out.println("分かりました 分かりました食事療法で少しでも生きられるんですね"); 93 } 94 95 void 抑うつ() { 96 System.out.println("嫌だ・・死にたくない・・・・耐えられない・・・・"); 97 slpl(); 98 for (int i = 0; i < 400; i++) { 99 System.out.print("死にたくない "); 100 } 101 System.out.println(); 102 } 103 104 void 受容() { 105 System.out.println("昨日お迎えが来る夢を見たんです"); 106 } 107 108 void slpl() { 109 try { 110 Thread.sleep(2000); 111 } catch (Exception e) { 112 } 113 } 114 115} 116 117class 死を待つ人3 extends 死 {// 抽象クラスを実装したクラス3 118 119 void 否認() { 120 System.out.println("昨日死の宣告を受ける夢を見た リアルだったが夢は夢だ そうだあれは夢だ"); 121 } 122 123 void 怒り() { 124 System.out.println("俺が死んだら残された皆はどうなるんだ! そんなの認められるはずがないだろうが!"); 125 } 126 127 void 取り引き() { 128 System.out.println("この万病に効くエキスさえあれば・・・"); 129 } 130 131 void 抑うつ() { 132 for (int i = 0; i < 25; i++) { 133 System.out.print("嫌だ・・・死にたくない・・・ "); 134 slpl(); 135 System.out.print("あの頃・・・食事に気を付けてさえいれば・・・ "); 136 slpl(); 137 System.out.print("これは夢だ・・・夢だ・・・ "); 138 slpl(); 139 } 140 System.out.println(); 141 } 142 143 void 受容() { 144 System.out.println("僕が死ぬことは最初から定められていた運命だったんです だからこれは仕方のない事なんです"); 145 } 146 147 void slpl() { 148 try { 149 Thread.sleep(50); 150 } catch (Exception e) { 151 } 152 } 153 154} 155 156class Jyuyou { 157 158 public static void main(String[] args) { 159 160 死 a[] = new 死[3];// 配列変数aに全ての実装クラスのインスタンスを一括して入れられる(多態性) 161 162 a[0] = new 死を待つ人1(); 163 a[1] = new 死を待つ人2(); 164 a[2] = new 死を待つ人3(); 165 166 for (int i = 0; i < 3; i++) {// このfor文の中で行われる操作は 167//a配列の中に入るクラス死の実装クラスをどれだけ増やしても同じで済む 168 169 a[i].set(i); 170 a[i].死の受容過程(); 171 172 } 173 174 } 175 176} 177
投稿2017/01/08 06:19

退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ベストアンサー
そもそも「プログラムの処理内容がまだ確定(未完成?)していない、詳細未定(未完成?)」というのは、しっかりと設計していないことであり、きちんと完璧に設計すればいいと思うのですが、なぜそんなことがあり得るんですか?
ここで言うところの「未確定」「未完成」というのは、コンパイラーから見た話であり、その実態は完璧に設計した結果そうなったのであります。
先端が付け替え可能なドライバーセットを想像してください。グリップだけでは何もできず、先端を付けなければ機能しませんが、その代わり、目的に応じた先端を付け替えることでいろんな種類のねじに対応させることができます。別の言い方をすると、目的に応じて先端を付け替えられるようなドライバーを設計したら、そのようなグリップになったということです。
このグリップ部分が抽象クラスと考えることができます(厳密には違うかもしれませんが考え方として)。つまり、派生させて目的に応じた機能を実装させることを前提として設計したクラスが抽象クラスです。
投稿2017/01/08 02:30
編集2017/01/08 02:31総合スコア5944
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

0
マウスを例にします。
マウス売り場にはボタンが3つ以上付いていたり、トラックボールであったり、USB接続できたり、Bluetooth接続できたりと多様性がありますよね。
しかし、マウスは下記の最低限の機能を持っています。
・右クリックボタンが付いている。
・左クリックボタンが付いている。
・移動する。
・PC等に接続する。
・クリックした情報を送信する。
・移動させた情報を送信する。
このように、マウスがマウスとして成り立つための機能を抽象クラスとして定義しておくことで、新商品を作るときにこのクラスを継承し、実装することでマウスとして成り立つことになります。
投稿2017/01/08 02:14
編集2017/01/08 02:15総合スコア18155
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
文字通り「抽象的」なクラスです。この性質上、このインスタンスを直接作ることはできません。継承した上でサブクラスのインスタンスを作るための中間的なものです。
例えるならば、「単独では用を成さない部品」みたいなものです。(厳密に書けばis a関係なのでhas a関係を例にするのは不適切ですが気にしないでください。)
また、「サブクラスに共通して使用できること」も重要です。プログラミングの都合上、「作業後期になるまで確定しない仕様」、「出荷後に判明するバグ」といったものにも対策が必要です。これらのため、後になってからプログラムを一部だけ切り替える機能が必要となります。ここでもこの性質が有効となります。
投稿2017/01/08 06:02
総合スコア4853
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
抽象メソッドとは、オーバーライドされる前提のメソッドで、
抽象クラスとは、抽象メソッドを含むクラスです。
未完成なわけじゃないです。あえて詳細を決めないだけです。
オーバーライドって何?と思ったらお手持ちの参考書を戻りましょう。
そこを読む段階に達していません。
"HelloWorld"を出力するプログラムを考えます。
出力先は、標準出力や、ファイルなどが選べるとします。
このとき、
java
1abstract class Writer{ 2 abstract void Write(String text); 3}
こんな抽象クラスを用意して、
class HelloWorld{ static void Greeting(Writer w){ w.Write("HelloWorld"); } }
こう使えば、渡された抽象クラスの実装によって何処に出力するか切り替えられます。
例えば標準出力版は
java
1class ConsoleWriter extends Writer{ 2 void Write(String text){ 3 System.out.println(text); 4 } 5}
java
1public class Main { 2 public static void main(String[] args) throws Exception { 3 Writer w = new ConsoleWriter(); // 実用的にはwの中身はいろいろ選択できるようにする 4 HelloWorld.Greet(w); 5 } 6}
ちなみにこの例だとinterfaceでいいんですが、
抽象クラス使ったほうが良いコードはコード長くなるのでこうしました。
投稿2017/01/08 05:32
総合スコア13553
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
参考情報:
-
2016年度プログラム言語(JAVA)抽象クラスとインターフェース http://ocw.tsukuba.ac.jp/courses/3137
-
抽象クラス(Abstract)とインターフェース(Interface)の違いと使い分けについて http://www.pepsinogen.blog/archive/2016/08/09
ポケモンGO で、メタモンやその他のポケモン(かくとうとかこおりの属性ももっている)を プログラミングで class 定義していたとしたら、どうなるかを考えてみると面白いかもしれない。
ジム戦では、メタモンはメタモンとして戦うことはできない。
(でもメタモン同士を戦わせたことがあるが、そのときはメタモン同士の戦いになったけど)
投稿2017/01/08 05:21
総合スコア22328
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
以下が4言語のオブジェクト指向操作のための命令一覧表です。10年前の編集なので、C#やUnityやythonなどは取り上げていません。このように各言語がオブジェクト指向プログラミングを実装しているという点では、OOPの必要性は大きいといえます。
4言語のオブジェクト指向操作のための命令一覧表(図の校正中)
各言語の癖はありますが、操作手順の大枠は共通しています。
投稿2017/01/08 04:56
編集2017/01/08 06:50総合スコア2287
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/01/08 05:27 編集
2017/01/08 05:38 編集

0
オブジェクトは道具の一般的な概念です。
例えば、電卓とは計算できて答を表示するものと当然のように分かっていますが、各電卓メーカーは各自が具体的な製品を製作し世に出します。つまり、抽象的な電卓はクラスにあたり、各社の製品はインスタンスにあたります。
実際に加える機能と取り除く機能を用意した電卓オブジェクトを定義します。変数はkotaeのみ、メソッドはコンストラクタも表示メソッドも省いたtasuとhikuの2つだけです。
Dentaku0.java
Java
1public abstract class Dentaku0<T> { 2 private Object kotae; 3 public <T> void tasu(){}; 4 public <T> void hiku(){}; 5}
この抽象クラスの電卓は、整数用なのか実数用なのかあいまいな電卓です。
このDentaku0から整数計算用の電卓と実数計算用の電卓を派生させ、それぞれ操作します。
Dentaku_seisuu.java
Java
1public class Dentaku_seisuu<D> extends Dentaku0<D> { 2 int kotae = 0; 3 public int tasu(int kazu) { 4 return kotae += kazu; 5 } 6 public int hiku(int kazu) { 7 return kotae -= kazu; 8 } 9}
この整数用電卓を操作する見本プログラムが次のコードです。
Dentaku_seisuu_Sousa1.java
Java
1public class Dentaku_seisuu_Sousa1 { 2 public static void main( String args[] ) { 3 Dentaku_seisuu<Object> d1 = new Dentaku_seisuu<Object>(); 4 d1.tasu(5); 5 d1.tasu(5); 6 System.out.println("d1の答は" + d1.tasu(0)); 7 8 Dentaku_seisuu<Object> d2 = new Dentaku_seisuu<Object>(); 9 d2.tasu(1); 10 d2.tasu(1); 11 System.out.println("d2の答は" + d2.tasu(0)); 12 } 13}
また、実数用の電卓をDenkatu0クラスから派生させれば、
Java
1public class Dentaku_jissuu<D> extends Dentaku0<D> { 2 double kotae = 0.0; 3 public double tasu(double kazu) { 4 return kotae += kazu; 5 } 6 public double hiku(double kazu) { 7 return kotae -= kazu; 8 } 9}
Denntaku_jisuuクラスを操る見本のコードが、
Dentaku_jissuu_Sousa1
Java
1public class Dentaku_jissuu_Sousa1 { 2 public static void main( String args[] ) { 3 Dentaku_jissuu<Object> d1 = new Dentaku_jissuu<Object>(); 4 d1.tasu(5.0); 5 d1.tasu(5.0); 6 System.out.println("d1の答は" + d1.tasu(0.0)); 7 8 Dentaku_jissuu<Object> d2 = new Dentaku_jissuu<Object>(); 9 d2.tasu(1.0); 10 d2.tasu(1.0); 11 System.out.println("d2の答は" + d2.tasu(0.0)); 12 } 13}
と記述できます。一通りeclipseで検証済みのコードです。
さて、このように数値の型ごとに部分部分でコードがかわってしまうような場合に、抽象クラスを決定しそれを作り込んでいくのです。
もう一つ文字列の足し算と引き算を実装したDentaku_mojiretsuクラスを書いてみました。足し算は単純に引数の文字列を継ぎ足すだけですが、引き算は、引数がオブジェクト内にあれば抜き去り、なければ書き換えずにエラーメッセージを戻します。
Dentaku_mojiretsu.java
Java
1public class Dentaku_mojiretsu<D> extends Dentaku0<D> { 2 String kotae = ""; 3 public String tasu(String mojiretsu) { 4 return kotae += mojiretsu; 5 } 6 public String hiku(String mojiretsu) { 7 if (kotae.matches(".*" + mojiretsu + ".*")) 8 return kotae = kotae.replaceAll(mojiretsu, ""); 9 else 10 System.out.println("'" + kotae + "'の中には、\n'" + mojiretsu + "'は見当たらないので、引けません。"); 11 return ""; 12 } 13}
この文字列加工用のDentaku_mojiretsuクラスを操る見本が次のコードです。
Dentaku_mojiretsu_Sousa1.java
Java
1public class Dentaku_mojiretsu_Sousa1 { 2 public static void main( String args[] ) { 3 Dentaku_mojiretsu<Object> d1 = new Dentaku_mojiretsu<Object>(); 4 d1.tasu("明けまして"); 5 d1.tasu("おめでとうございます。"); 6 System.out.println("最初の加算のd1の答は「" + d1.tasu("") + "」です。"); 7 d1.hiku("ございます"); 8 System.out.println("つぎの加算のd1の答は「" + d1.tasu("") + "」です。"); 9 10 Dentaku_mojiretsu<Object> d2 = new Dentaku_mojiretsu<Object>(); 11 d2.tasu("寿限無、寿限無、"); 12 d2.tasu("五劫のすり切れ"); 13 System.out.println("最初の加算のd2の答は「" + d2.tasu("") + "」です。"); 14 d2.hiku("海砂利水魚"); 15 System.out.println("つぎの加算のd2の答は「" + d2.tasu("") + "」です。"); 16 } 17}
このように文字列での足したり引いたりする操作を実装することもあるわけです。
他にも、日付の加減算、時刻の加減算、画像の加減算、角度の加減算などとさまざまな派生処理が起こりえます。後手後手の作業で、最初に作った整数用電卓をオーバーライドしてクラスを多様化したり、多態性を活用して引数を違えたメソッドを並べておくこともあることでしょう。
ともかく、ある抽象的な機能を特定のメソッドや変数で定義しておくと派生事項が膨らんでも見通しが良くなります。
抽象的オブジェクトとその派生オブジェクトとの関係を、マンションの間取りとリフォームした部屋として例えたら、部屋の大きさや窓の位置や水回りや柱の大きさや位置は事前に決まっていてこれが抽象的オブジェクトにあたります。そして、入居する人が住居用かオフィス用か押し入れ用かなど用途によって、壁の色や間取りの変更や風呂の特注などのリクエストを取り入れカスタマイズされた部屋の図面が派生クラスに当たります。そして入居可能になった各部屋がインスタンスに当たります。
今回は複数のマイナス評価もいただき、正直焦りましたが、自分なりにOOPの理解が深まり、Dentaku0クラス以下の教材の蓄積ができたので実に勉強になりました。時刻計算用の電卓の実装などまた時間があったら取り組んでみます。
投稿2017/01/08 03:16
編集2017/01/09 10:01総合スコア2287
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/01/08 03:48
2017/01/08 09:01
2017/01/08 21:32 編集
2017/01/09 09:00

あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。