Author: switch over

A big outdoor sports lover & A IT engineer.

5.演算子

5章 演算子
1.演算子の種類
 ●演算子
   ○算術演算子
    ・四則演算子         +,-,*,/,%
    ・符号変換           -
   ○比較演算子
    ・大小比較           <,>,<=,>=
    ・等価、非等価         ==,!=
   ○論理演算子
    ・論理否定           !
    ・論理積            &&
    ・論理和            ∥
   ○インクリメント演算子     ++
   ○デクリメント演算子      –
   ○ビット演算子
    ・論理積            &
    ・論理和            |
    ・排他的論理和        ^
    ・論理否定(1の補数)    ~
   ○シフト演算子
    ・左シフト            <<
    ・右シフト            >>
   ○代入演算子
    ・代入              =
    ・四則演算           +=,-=,*=,/=,%=
    ・シフト演算           <<=,>>=,>>>=
    ・ビット演算           &=,|=,^=
   ○cast演算子           (型) 例えば、(int),(char)など
   ○順次演算子            ,
   ○条件演算子(三項演算子)   ? : 
   ○instanceof演算子       instanceof
 
2.インクリメント演算子、デクリメント演算子
   int i;
       for( i = 0 ; i < 10 ; i = i +1 )
             System.out.println(i);
上記の例のように、今まで変数を1増やす場合「 i = i + 1 」と書いてきた。しかし「変数を1増やす」という作業は頻繁に行われるため、特別の演算子が用意されている。上の例は、その演算子を使用すると下記のようになる。
   int i;
       for( i = 0 ; i < 10 ; i++ )
             System.out.println(i);
この「++」がインクリメント演算子である。逆に「変数の値を1減らす」演算子はデクリメント演算子といい「――」である。
この演算子は「i++」のように変数の後に書くことも、「++i」のように変数の前に書くこともできる。しかし「j=i++」のように式の中に組み込むと、変数の前に書いた場合と、変数の後に書いた場合の動作が異なる。変数の前に書いた場合は、式を評価する前にインクリメントする。逆に変数の後に書いた場合は、式を評価した後にインクリメントする。
import java.io.*;

    public static void main( String args[ ] ){
        int a,b;
        a = 3;
        b = ++a;

        System.out.println("a=" + a + " b=" + b);
    }
}

import java.io.*;

class Test{
     public static void main(String args[ ] ){
        int a, b;
        a = 3;
        b = a++;

        System.out.println("a=" + a + " b=" + b);
    }
}

D:\java>javac Test.java

D:\java>java Test

a=4 b=4

D:\java>

D:\java>javac Test.java

D:\java>java Test

a=4 b=4

D:\java>

 一つの式の中に同じ変数に対するインクリメント演算子が複数あってはならないということである。もちろんデクリメント演算子も同じ注意がある。
例えば、「b=a++ +a++」というのはダメ。予期しない結果になることが多く、バグを招き兼ねない。「printf("%d %d\n",++a,++a);」というのもダメ。
import java.io.*;

class Test{
    public static void main(String args[ ] ){
       int a, b;
       a = 0;
       b = ++a + ++a;

        System.out.println("a=" + a + " b=" + b);
    }
}

import java.io.*;

class Test{
     public static void main(String args[ ] ){
        int a, b;
        a = 0;
        b = a++ + a++;

        System.out.println("a=" + a + " b=" + b);
    }
}

D:\java>javac Test.java

D:\java>java Test

a=2 b=3

D:\java>

D:\java>javac Test.java

D:\java>java Test

a=2 b=1

D:\java>

 
3.複合代入演算子
 複合代入演算子とは、演算と代入を一つの演算子にまとめたものである。インクリメント演算子やデクリメント演算子も複合代入演算子の一種ということができる。
複合代入演算子 使用例 意味 普通の演算子
+= a+=b; aにbを加えた値をaに代入する a-a+b
-= a-=b; aからbを引いた値をaに代入する a=a-b
*= a*-b; aとbを乗じた値をaに代入する a=a*b
/= a/=b; a÷bをaに代入する a=a/b
%= a%=b; a÷bの余りをaに代入する a=a%b
>>>= a>>>=b; aをbビット右にシフトした値をaに代入する(符号なし整数、ビット列) a-a>>b
>>= a>>=b; aをbビット右にシフトした値をaに代入する(符号付き整数) a=a>>b
<<= a<<=b; aをbビット左にシフトした値をaに代入する a=a<<b
&= a&=b; aとbの論理積をaに代入する a=a&b
|= a|=b; aとbの論理和をaに代入する a=a|b
^= a^=b; aとbの排他的論理和をaに代入する a=a^b
         
4.ビット演算子
演算子 意味 使用法
& ビット積(AND) a=b&c; bとcの各ビット毎の論理積をとり、その結果をaに代入する
| ビット和(OR) a=b|c; bとcの各ビット毎の論理和をとり、その結果をaに代入する
^ ビット排他的論理和(XOR) a=b^c; bとcの各ビット毎の排他的論理和をとり、その結果をaに代入する
~ ビット否定(NOT) a=~b; bの各ビットを反転しその結果をaに代入する
AND      
b c 結果(a)  
0 0 0  
0 1 0  
1 0 0  
1 1 1  
AND演算はビットを0でマスクするのに使用する。マスクとはあるビットを0もしくは1に変えること。例えば、奇数か偶数かを調べるためには、最下位を残して0でマスクする。その値が1なら奇数、0なら偶数ということになる。
import java.io.*;

class Test{
     public static void main(String args[ ]) {
         int i = readNumber( );

         if ( i = & 1 == 1 ) {
              System.out.pringtln("奇数です");
    }
         else{
              System.out.println("偶数です");
    }
  }

    // キーボードから数字を入力するメソッド
  public static int readNumber( ){
        byte b[ ] = byte [100];

        try{
              System.out.println.in.read(b);
            return Integer.parseInt((new String( (b).trim( ) ).trim( ) );
        }catch(Exception e) {
                return 0;  
          }
     }
}

OR      
b c 結果(a)  
0 0 0  
0 1 1  
1 0 1  
1 1 1  
OR演算はビットを1でマスクするのに使用する。
XOR      
b c 結果(a)  
0 0 0  
0 1 1  
1 0 1  
1 1 0  
XOR演算はビットを必要な部分だけ反転させるのに使用する。
NOT      
b 結果(a)    
0 1    
1 0    
NOT演算は1の補数を表す。1の補数とはすべてのビットを反転する。
 
5.シフト演算子
 シフト演算とは論理演算と同じようにビットを操作する命令である。具体的には、2進数の各ビットをずらすことである。右にずらすか左にずらすか、符号なしか、符号付きかによって3種類の演算子が用意されている。
演算子  意味 使用法
<< 左にシフト a = b << c bを左にcビットずらす
>>  右にシフト a = b >> c bを右にcビットずらす(符号付き)
>>> 右にシフト a = b >> c bを右にcビットずらす(符号なし)
0000 0001 = 1        1111 1111 = -1
   ↓                         ↓
0000 0010 = 2        1111 1110 = -2
       ↓            ↓
0000 0100 = 4           1111 1100 = -4

      整数          負数
このように左にずらすと、その値は2倍になる。
 
6.キャスト演算子
 キャストとは「型変換」のこと。下の例のように代入元と代入先の変数の型が異なる場合、型変換を行う必要がある。一般に「byte」型から「int」型への変換など、小さい入れ物から大きな入れ物へは自動的にキャストされる。これを「暗黙の型変換」という。
   int    i = 100;
   byte b = 100;

  i = b;            // 暗黙の型変換

 
 逆に大きな入れ物から小さな入れ物へのキャストは、データが失われる可能性があるため、暗黙の型変換は行われずえにエラーが出てしまう。このような場合、コンパイラに「データが失われるかもしれないのは、わかっているよ」と教えなければならない。これを「明示的な型変換」という。
   int    i = 100;
   byte b = 100;

  b = i;            // エラー
  b = (byte)i;  // 明示的なキャスト

 
また、整数と小数(実数)の関係も場合によっては明示的な型変換が必要になる。下の左の例では、明示的な型変換はしていない。右の例では明示的な型変換をしている。キャストしなければ割り算の結果は整数になってしまう。
import java.io.*;

class Test{
    public static void main(String args[ ] ){
       int a = 300;
       float f = a / 7;
 
       System.out.println( f );
    }
}

import java.io.*;

class Test{
     public static void main(String args[ ] ){
        int a = 300;
        float f = (float)a /7;

        System.out.println( f );
    }
}

D:\java>javac Test.java

D:\java>java Test
42.0

D:\java>

D:\java>javac Test.java

D:\java>java Test
42.857143

D:\java>

 
7.順次演算子
 カンマで区切られたいくつかの式を左から順に実行する。このカンマのことを順次演算子という。
import java.io.*;

class Test{
      public static void main(String args[ ]){
      int i;
      int j;

      for ( i = 0, j = 10 ; i < 10 ; i++, j– )
            System.out.println(i*j);
      }
}

 

C:\java>javac Test.java

C:\java>java Test
0
9
16
21
24
25
24
21
16
9

C:\java>java

 

6.オブジェクト指向とは

 

6章 オブジェクト指向とは
1.オブジェクト指向とクラス
 「オブジェクト指向」では、処理は二の次である。一番に考えるのはオブジェクトである。オブジェクトというのは「モノ」であり、実際に存在するもののことをいう。クラスとオブジェクトは等価。オブジェクトには決められた仕事や処理が決まっている。これがクラスで定義するメンバーメソッドである。
例)色鉛筆は実際に存在するものである。したがって、オブジェクトつまり「色鉛筆クラス」になる。(色鉛筆構造体)色鉛筆には様々な色があるメンバー変数として「色」「芯の長さ」を決める。
class Color{
    
int R;
    
int G;
    
int B;
}

class ColorPen{
    Color color;
    
int  length;
}

色鉛筆は、絵を描くことができる。メンバーメソッドを定義する。 
class Color{
    
int R;
    
int G;
    
int B;
}

class ColorPen{
    Color   color;
    
int    length;

     void draw( ){
       :
       :
    }
}

 オブジェクト指向では、このように「モノ」と「それを扱う関数(メソッド)」が一緒にクラスとして定義されているので、「色鉛筆で食べ物を食べる」というミスは文法的に書けないことになっている。

 整理すると「オブジェクト指向」では「モノ(データ、デバイスなど)を第1優先に考える」という分析・設計・実装方法である。

 
2.継承
 色鉛筆は鉛筆の一種であると考えられる。鉛筆でも絵を描くことができるが、色は黒一色である。色鉛筆では様々な色で絵を描くことができる。
class Color{
    
int R;
    
int G;
    
int B;
}

class Pencil{
    
int    length;

     // 黒で絵をかく
     void draw( ){
       :
       :
    }
}

class ColorPen extends Pencil{
       Color color;

// int  length; スーパークラスで定義されているので、ここでは定義しない

     // colorで絵をかく
    void draw( ){
       :
       :
    }
}

 このように定義すると「鉛筆クラスは鉛筆クラスの一種」という意味になる。このように、既存のクラスの一種であるクラスを定義することを「継承」という。色鉛筆クラスには「長さ」定義されていないが、鉛筆クラスで定義されているの
で、色鉛筆クラスにも「長さ」が定義されていることになる。

 この場合、色鉛筆クラスからみて鉛筆クラスを「スーパークラス」、「基底クラス」あるいは「親クラス」といい、鉛筆クラスから見て色鉛筆クラスを「サブクラス」あるいは「子クラス」という。

 上のクラス図は、基底クラスとして「哺乳類クラス」、その子クラスとして「犬クラス」「猫クラス」そして「象クラス」を定義している。さらに「猫クラス」の子クラスとして「三毛猫クラス」「シャム猫クラス」を定義している。
 しかし、「犬クラス」の子クラスとして「盲導犬クラス」を定義することはあまりよくない。なぜならこの場合、動物の種類として継承しているため、そこに「警察犬」「盲導犬」といった機能で継承することになるからである。
 Javaでは、機能で継承させたい場合には、継承ではなくインターフェースという機能を使う。詳細は後述。

 
3.カプセル化
 クラスのメンバー変数、メンバーメソッドは誰が触っても良いものと、触るクラスを限定したい場合がある。誰が触っても良いものを「public」、自分と同じパッケージ内のクラスからもしくはサブクラスからしか触れられない物を「protected」、そして自分自身のクラスからしかさわれない物を「private」という。
 すべてのメンバー変数、メンバーメソッドをpublicにして、プログラマーたちが決め事としてさわらないようにと決めても良いが、中には決め事を守らないプログラマーもいるため、public、protected、privateを決めておけば文法的にコンパイラでチェックしてくれる。
 何故そんなことをするのだろう。これは、作ったプログラムの一部を部品化しやすいようにするためである。オブジェクト指向では、作ったプログラムを部品化しておき、別のプログラムを作る際に再利用できるようにする。例えばインターネットブラウザのプログラムを作った場合、ネットワーク関係の部品は、次に作るメーラーに再利用ができる。このように部品化したクラスやパッケージは使い方さえ知っていればそのソースコードを知らない人でも使うことができる。ところが、内部(ソースコード)を知らないプログラマーが出てくるとバグの原因になる。そこで、protectedであれば勝手なことはされなくて済む。
 基本的にメンバー変数はすべてprivateかprotectedにする。そして、参照するためのメソッドと代入するメソッドを用意する。このようなメソッドをアクセサメソッドと呼ぶ。なぜ、こんな面倒なことをするのか、理由は3つある。

 1、メンバー変数の型を「int」から「double」に変更しようとした場合、参照する場合と代入する場合の2つに影響してくる。publicな変数だとプログラムすべてに渡ってこの変数を参照する場所と代入する場所を探すのは面倒な作業である。それに対しメソッドを探すのは比較的容易だからである。
2、メンバー変数は他のクラスから参照しても良いが値を変更するのは×という変数があったとする。この場合、参照するメソッドはpublicに、代入するメソッドはportectedかprivateにすればよい。
3、マルチスレッドプログラムにした場合、複数のスレッドが同じ変数にアクセスする場合がある。例えば、あるスレッドがリストをソース中に、別のスレッドがそのリストを参照した場合、矛盾が生じてしまう。このような場合は「セマフォ」というリストを参照する権利を与え権利があるスレッドだけがリストを参照したりさわったりできるうようにする。あらかじめ参照するためのメソッドを用意しておけばセマフォの処理を組み込む場合もそのメソッドをちょっと直すだけなので簡単である。
 このように変数やメソッドにアクセスできるクラスを制御することで、あるクラスの部品のようにすることができる。部品のようになったクラスは別のプログラムを作るときにも利用できる。例えば、Javaでは標準でGUIや通信mその他さまざまなクラスがたくさん用意されており、通信をしたいと思えばそのためのクラスをファイル操作したければそのためのクラスをプログラマーが使用するだけだから、プログラムが簡単に書ける。また、携帯電話用のソフトを書きたければ携帯電話会社が公開しているクラス群が利用できる。
 また、通信をするためのクラスを利用しようと思った場合、通常その中身がどんなプログラムになっているのか、プログラマーは知る必要がない。つまり、通信をするためのクラスの中身はブラックボックスということになる。
 このように、中身を知らなくても部品のように使えるように設計することを「カブセル化」という。
 
4.ポリモーフィズム
 ポリモーフィズムとは、ギリシャ語で「多数の形態」という意味である。プログラムで「ポリモーフィズム」というと同じメソッドを定義することをいう。
 例に例えると「鉛筆クラス」の「絵を描くメソッド」と「色鉛筆クラス」の「絵を描くメソッド」は、若干機能がことなるが「絵を描くメソッド」というメソッドの名前は同じである。
 例えばint型の絶対値を求めるメソッドを「abs」と名づけると小数も扱えるようなメソッドを追加しようとした場合、「abs_float」と名前を変えなくてはならなかった。Javaではポリモーフィズムによって、引数が異なれば同じ名前のメソッドを複数定義することもできる。このように同じ名前のメソッド(またはコンストラクタ)を作ることを、メソッドをオーバーロードするという。
 

8.継承

8章 継承
1.継承とは
 継承とは、あるクラス(A_Class)があるクラス(B_Class)の一種である場合、A_ClassはB_Classを継承しているという。そして、A_ClassはB_Classの子クラス・サブクラス・派生クラスといい、逆にB_ClassはA_Classの親クラス・スーパークラス・基底クラスという。
 これをJavaで定義すると以下のようになる。
class B_Class{
     :
     :
} 
 
class A_Class extends B Class 
   :
   :
}
 
 「一種である」というのは、下記の図形の例のように「楕円」や「線」や「多角形」は「図形」の一種であり、「円」は「楕円」の一種。「直線」は「線」の一種であるし、「三角形」や「四角形」は「多角形」の一種であるということである。

つまり、「三角形」や「四角形」は「多角形」を継承しているといい、「三角形」や「四角形」は「多角形」の子クラス(サブクラス)であるといい、「多角形」は「三角形」や「四角形」の親クラス(スーパークラス、基底クラス)である。

また、「正方形」は「菱形」と「長方形」の両方の特別な場合であり、このような継承の仕方を多重継承という。Javaのようにオブジェクト指向言語でも多重継承をサポートしていない。

 
 継承すると、親クラスのメンバー変数、メンバー関数は、その子クラスでも使用できるようになる。子クラスでは親クラスのメンバー関数をオーバーライトすることができるため子クラスで実相をより高機能化することができる。
 
2.スーパークラスのコンストラクタ
 クラスを継承して子クラスを作る場合、そのほとんどが親クラスのコンストラクタを明示的に呼び出す必要がある。つまり親クラスのデフォルトコンストラクタ以外を呼び出したい場合などである。
 StringVectorクラスと、ソース昨日付きVectorクラス(SortableStringVectorクラス)の場合を考えてみる。SortableStringVectorクラスのコンストラクタに、配列の大きさを指定すると、その引数を親クラスのコンストラクタに通知しなければならない。その場合は「super( )」を使う。このsuperは、コンストラクタの最初にしか用いることはできない。
Test.java
class Test{ 
    public static void main(String args[ ]){
         SortableStringVector vect = new SortableStringVector( );  
         String str;
          int      i = 0;
          while (true){
              str = readLine( );
              if( str.length( ) == 0 )
                  break;

              vect.at( i,str );
              i++;
           }

           vect.sort( );

           int n = vect.getLength( );
           for ( i = 0 ; i < n ; i++ ){
              str = vect.at( i );
              if( str.== null )
                  break;
              System.out.print(str);
           }
       }

           // 1行読み込んで、String型として返す
       public static String readLine( ){
          byte b[ ] = new byte[1024];
             try{
                 System.in.read(b);
                 } catch(Exception e){
                      return null;
                 }
 
                 int i;
                 for( i = 0 ; b[ i ] != 0 && i < b.length ; i++ );
                return new  String(b, 0, i);
                }
}
  

StringVector.java

public class StringVector{ 
     ///////////////////////////////////////////////////
     // 管理情報

    protected int m_size;                           // 配列サイズ
    protected String m_vector[ ];               // 配列

     // コンストラクタ:引数あり
     // StringVector オブジェクトが定義された時に自動的に呼び出される
    public StringVector( int sz );
       // 配列サイズを保存する
         m_size = sz ;

       // 配列の実体(size個のint配列)を自由記憶上に割り当てる
         m_vector = new String[m_size] ;
     }
  
  // コンストラクタ:引数なし
  // StringVector オブジェクトが定義された時に自動的に呼び出される
    public StringVector( ){
       // もう1つのコンストラクタを呼び出す
    this ( 100 );
     }

  // 代入
    public void at( int n,String value ){
          if ( n >= m_size || n < 0 ){
               System.err.println("invalid index");
               System.exit( 1 );       // 異常終了する
            }

            m_vector[n] = value;
            }

  // 取得
    public String at(int n){
           if ( n >= m_size || n < 0 ){
               System.err.println("invalid index");
               System.exit( 1 );       // 異常終了する
           }

               return m_vector[n];
     }
  // 現在の配列サイズを獲得する
      public int getLength( ){
               return m_size;
     }
}
  

SortableStringVector.java

public class SortableStringVector extends StringVector{
     ///////////////////////////////////////////////////
     // 管理情報

     // コンストラクタ:引数あり
     // SortableStringVector オブジェクトが定義された時に自動的に呼び出される
    public SortableStringVector( int sz ){
              // 親クラスのコンストラクタ呼び出し
             super(sz);
     }
 
  // コンストラクタ:引数なし
  // StringVector オブジェクトが定義された時に自動的に呼び出される
    public SortableStringVector( ){
       // もう1つのコンストラクタを呼び出す
    this ( 100 );
     }

  // バブルソート
    void sort( ){
          int flag = m_size -1;
          int flag = m_size;
          for ( ; j > 0 && flag ! = 0 ; j– ){
                 int i = 0;
                   flag = 0;
                 for ( ; j > i ; i++ ){
                             if ( m_vector[ i ] ! = null && m_vector[i+1] != null && m_vector[i+1].compareTo(m_vector[i]) > 0 ){
                            String a = m_vector[i+1];
                           m_vector[ i+1 ] = m_vector[i];
                           m_vector[ i ] = a;
                           flag = 1;
                       }
                 }
           }
     }
}
  

C:\java>javac Test.java

C:\java>java Test
123
234
345
222

345
234
222
123

C:\java>

 このように作成されたSortableStringVectorは、その親クラスであるStringVectorクラスのメソッドを使用することもできるし、新たに加えたsortメソッドも利用できる。親クラスを進化させることができる。
 
3.継承とキャスト
 下記のようにAクラスがBクラスの親クラスだった場合、Aクラス用の変数にBクラスのインスタンスを代入することができる。しかし、逆はエラーになる。犬は哺乳類だが、哺乳類は犬ではない。
class A{
       :
       :     
}
 
class B extends A{
       :
       :     
}
 
class Test{
      public static void main(String args[ ] ){
            // これはOK
      A a = new B( );

            // これはエラー
      B b = new A( );
     }
}
  

 これまで、StringVectorクラスを作ってきたが、何もStringクラスに特化させる必要はなかったのである。次のようにすればStringクラス以外のクラスもベクターとして管理することができる。
 JavaではすべてのクラスがObjectクラスの子クラスである。最初から用意されているStringクラスはもちろん、これまで作ってきたStringVectorクラスやSortableVectorクラスも実はObjectクラスを継承している。

したがって、下記のようにStringVectorクラスをObjectVectorクラスに書き換えたベクタークラスを用いて今まで通りStringVectorのように使うことができる。

Test.java

class Test{
    public static void main( String args[ ] ){
        ObjectVector vect = new ObjectVector( );
        vect.at(0, "HogeHoge");
        vect.at(1, "HonyoHonyo");

          System.out.println(vect.at( 0 ));
          System.out.println(vect.at( 1 ));
     }
}
  

ObjectVector.java

// StringVectorStringをObjectに変更したベクタークラス
public class ObjectVector{
    //////////////////////////////////////////////
    // 管理情報

        protected int m_size;                // 配列サイズ
        protected Object m_vector[ ];  // 配列

    // コンストラクタ:引数あり
    // ObjectVector オブジェクトが定義された時に自動的に呼び出される
          public ObjectVector( int sz ){
    // 配列サイズを保存する
          m_size = sz ;
 
    // 配列の実体(size個のint配列)を自由記憶上に割り当てる
          m_vector = new Object[m_size] ;
        }

    // コンストラクタ:引数なし
    // ObjectVector オブジェクトが定義された時に自動的に呼び出される
          public ObjectVector(  ){
         // もう1つのコンストラクタを呼び出す
             this (100) ;
        }

    // 代入
          public void at( int n, Object value ){
                 if ( n >=  m_size || n < 0 ){ 
                  System.err.println("invalid index");
                  System.exit(1);          // 異常終了する
         }

         m_vector[n] = value;
    }

    // 取得
          public Object at(int n){
                 if ( n >= m_size || n < 0 ){
                  System.err.println("invalid index");
                  System.exit(1);          // 異常終了する
         }

             return m_vector[n] ;
        }

    // 現在の配列サイズを獲得する
          public int getLength( ){
                 return m_size;
       }
}
  

 
Test.java

class Test{
    public static void main(String args[ ] ){
         ObjectVector vect = new ObjectVector( ){
         vect.at(0,"HogeHoge");
         vect.at(1,"HonyoHonyo");
 
             String str1 = vect.at(0);
             String str2 = vect.at(1);
      }
}
  

C:\java>javac Test.java
Test.java:7 Incompatible type for declaration.Explicit cast needed to convert 
java.lang.Object to java.lang.String.
          String str1 = vect.at(0);

Test.java:8 Incompatible type for declaration.Explicit cast needed to convert
          String str2 = vect.at(1);
2 errors

C:\java>

 これは、ObjectVectorクラスのatメソッドの戻り値がObjectクラスだからである。前述したようにStringクラスはObjectクラスの一種だが、ObjectクラスはStringクラスの一種ではないため、Objectクラスとして返された戻り値はStringクラスに代入できない。

 しかし、プログラムをみればお分かりのとおり、実際に戻ってくるのはStringクラスのインスタンスである。この場合は、下記のようにキャストする。

Test.java

class Test{
    public static void main(String args[ ] ){
         ObjectVector vect = new ObjectVector( ){
         vect.at(0,"HogeHoge");
         vect.at(1,"HonyoHonyo");
 
             String str1 = (String)vect.at(0);
             String str2 = (String)vect.at(1);
      }
}
  

C:\java>javac Test.java

C:\java>

 では、Stringクラスのインスタンスが入っていないのに、Stringクラスにキャストしようとするとどうなるのろうか。次の例では、StringクラスではなくTestクラスのインスタンスをObjectVectorに入れて、Stringクラスにキャストして取り出そうとしている。  
Test.java

class Test{
    public static void main(String args[ ] ){
         ObjectVector vect = new ObjectVector( ){
         vect.at(0,"HogeHoge");
         vect.at(1," new Test( ));
 
             String str1 = (String)vect.at(0);
             String str2 = (String)vect.at(1);
      }
}
  

C:\java>javac Test.java

C:\java>java Test.java
Exception in thread "main" java.lang.ClassCastException: Test
           at Test.main(Test.java:8) 

C:\java>

 文法的には間違ってないのでコンパイルは正常にできるが、実際に実行しようとした場合にキャストできないとエラーとなってしまう。

 このようなことにならないようにプログラマーは気をつけなければならないが、下記のようにinstanceof演算子を用いて、ミスしても大丈夫なようにするべきである。

Test.java

class Test{
    public static void main(String args[ ] ){
         ObjectVector vect = new ObjectVector( ){
         vect.at(0,"HogeHoge");
         vect.at(1," new Test( ));
 
             String str1 = null ;
             String str2 = null ;
             Object obj; = null ;

          obj = vect.at(0);
          if( obj instanceof String )
              str1 = ( String )obj;

          obj = vect.at(1);
          if( obj instanceof String )
              str2 = ( String )obj;
    }
}
  

C:\java>javac Test.java

C:\java>java Test

C:\java>

 
4.スコープ 
 同じ変数や関数名を、親クラス、子クラスで定義するとどのようになるのだろうか。コンパイラは次のような順番で、目的とする変数や関数を検索する。
 派生クラスをつくり、その派生クラスのメンバー関数での例をしめす。
  ・派生クラスのメンバーメソッド内で定義した変数
  ・派生クラスのメンバー変数
  ・親クラスのメンバー変数 
// 親クラス
class Base{
    public    int a;
    private   int b;
}
 
// 子クラス
class Deriv extends Base{
    public    int b;
    private   int c;

    public void member_func_deriv( ){
             a = 1;               // 派生クラスにはないので、親クラスのaを参照
            b = 2;               // 派生クラスのbを参照
            c = 3;               // 派生クラスのcを参照
 
           super.a = 10;     // Baseクラスのaを参照
//      super.b = 20;     // Baseクラスのbを参照しようとするが、privateなのでエラー

     public static void main(String args[ ]){
           Deriv d = new Deriv( );

            d.a = 10;               // 派生クラスにはないので、親クラスのaを参照
            d.b = 10;               // 派生クラスのbを参照
            d.c = 10;               // 派生クラスのcを参照しようとするが、privateなのでエラー

//      d.super.a = 10;       // このような使い方はできない
  }
}
 
  

 
5.クラスの作成 
クラスの構築
 
・領域が確保される
 ・派生クラスの引数リストの一致するコンストラクタを呼び出す
 ・superがあれば、親クラスの引数リストの一致するコンストラクタを呼び出す
 ・superがなければ、親クラスの引数リストのないコンストラクタ(デフォルトコンストラクタ) を呼び出す
 ・派生クラスのコンストラクタの続きが実行される
※親クラスが他のクラスの派生クラスならば、再帰的に繰り返される
//親クラス
class Base{
    public Base( ){
             System.out.println("Base:Base( )");
      }
    public Base( int i ){
             System.out.println("Base:Base( int )");
    }
}

//子クラス
class Deriv extends Base{
    public Deriv( ){
             System.out.println("Deriv::Deriv( )");
    }
    public Deriv( int i ){
          super ( i );
             System.out.println("Deriv::Deriv( )");
    }

    public static void main( String args[ ]  ){
             System.out.println("main");
          Deriv d1 = new Deriv( );
          Deriv d2 = new Deriv( 1 );
    }
}
 

C:\java>javac Deriv.java

C:\java>java Deriv
main
Base::Base( )
Deriv::Deriv( )
Base::Base( int )
Deriv::Deriv( int )

C:\java>

 
6.abstarct 
 abstractとはメソッドの定義だけしておき、そのクラスでは実装せずに子クラスで実装するためのもである。abstractを含むクラスは完成していないクラスとなるために、インスタンスは作られない。子クラスでそのメソッドを実装し、子クラスのインスタンスを作る。
 abstractなクラスは、「class」の前に「abstract」をつけないと、コンパイルエラーになってしまう。
//親クラス
abstract class Base{
    abstract
public void func( );

//子クラス
class Deriv extends Base{
    public void func( ){
         System.out.println("Deriv::func");

    }

    public void main( String args[ ]){
          Base base1 = new Deriv( );      // OK
          Base base2 = new Base( );      // エラー
    }
}

基底クラスに純粋仮想関数が複数ある場合、すべての純粋仮想関数を派生クラスで実装しなければ派生クラスもまた抽象クラスになる。 
//親クラス
abstract class Base{
    abstract
public void func1( );
    abstract public void func2( );

//子クラス
abstract class Deriv extends Base{
    public void func1( ){
         System.out.println("Deriv::func1");
    }
}

//さらに子クラス
class DDeriv extends Deriv{
    public void func2( ){
         System.out.println("DDeriv::func2");
    }
    public void main( String args[ ] ){
          Base base1 = new DDeriv( );      // OK
          Base base2 = new Deriv( );       // エラー
          Base base3 = new Base( );       // エラー
    }
}

 抽象クラスは概念として導入する場合が多い。つまりお絵かきソフトを作ろうと考えた場合、図形データはObjectVectorクラスに入れる。図形データは以下のように作る。
//図形クラス(抽象クラス)
abstract class Zukei{
   
protected int x1, x2, y1, y2;

    //描画する関数
  public abstract void draw( );

    //セーブする関数
  public abstract void save( String filename);
}

//四角形クラス
class Rectangle extends Zukei {
    
//描画する関数(このクラスで実装する)
  public void draw( ){
         :
         :
    }

    //セーブする関数(このクラスで実装する)
    public void save( String filename){
         :
         :
    }
}

//直線クラス
class Line extends Zukei {
    
//描画する関数(このクラスで実装する)
  public void draw( ){
         :
         :
    }

    //セーブする関数(このクラスで実装する)
    public void save( String filename ){
         :
         :
    }
}

//円クラス
class Circle extends Zukei {
    
//描画する関数(このクラスで実装する)
  public void draw( ){
         :
         :
    }

    //セーブする関数(このクラスで実装する)
    public void save( String filename ){
         :
         :
    }
}

class Draw{
     public static void main( String args[ ]){
     ObjectVector vect = new ObjectVector( );
     vect.at(0, new Line( ));
     vect.at(1, new Circl( ));
    }
}

 
7.継承と包含
 新しいクラスを作成しようとした際には、次の3つの選択肢がある。
  1.まったくゼロから作る。
  2.既存のクラスを継承して、派生クラスとして作る。
  3.既存のクラスを包含して新しく作る。