- お知らせ -
  • 当wikiのプログラムコードの表示を直してみました(ついでに長い行があると全体が下にぶっ飛ぶのも修正)。不具合があればBBSまでご連絡下さい。

Java/プログラミング言語Java

はじめに Edit

クラスを拡張して、複数の形態をもつ機能のことをポリモフィズム(多態)という。

クラスを拡張した物をサブクラスもしくは、拡張したクラスという。
拡張した元のクラスをスーパークラスという。

メンバーがどのように振舞うかは、契約と呼ばれる。

継承は、契約や型の継承と、実装の継承がある。

インターフェスを使用することで、実装に関係のない型を定義できる。
コンポジションや、転送を利用して、実装の再利用も出来る。

3.1 拡張したクラス Edit

練習問題3.1 Edit

public class PassengerVehicle extends Vehicle {
    private int seatingCapacity;    // 座席数
    private int seatedPerson;       // 座っている人の数
    
    public void setSeatingCapacity(int value) { seatingCapacity = value; }
    public int getSeatingCapacity() { return seatingCapacity; }

    public void setSeatedPerson(int value) { seatedPerson = value; }
    public int getSeatedPerson() { return seatedPerson; }
    
    public PassengerVehicle(String firstOwner) {
        super(firstOwner);
    } 
    
    public String toString() {
        return super.toString() + "\n" 
            + "座席数 = " + seatingCapacity + "\n"  
            + "座っている人の数 = "+ seatedPerson;
        
    }
    
    public static void main(String[] args) {
        PassengerVehicle bicycle = new PassengerVehicle("Mike");
        bicycle.setVelocity(0.1);
        bicycle.setSeatingCapacity(1);
        bicycle.setSeatedPerson(1);
        System.out.println(bicycle + "\n");
        
        PassengerVehicle bike = new PassengerVehicle("Ken");
        bike.setVelocity(0.1);
        bike.setSeatingCapacity(1);
        bike.setSeatedPerson(1);
        System.out.println(bike + "\n");

        PassengerVehicle car = new PassengerVehicle("Joe");
        car.setVelocity(0.1);
        car.setSeatingCapacity(4);
        car.setSeatedPerson(2);
        System.out.println(car);
    }
}

結果は以下の通り。

ID = 0
スピード = 0.1
方向 = 0.0
所有者 = Mike
座席数 = 1
座っている人の数 = 1

ID = 1
スピード = 0.1
方向 = 0.0
所有者 = Ken
座席数 = 1
座っている人の数 = 1

ID = 2
スピード = 0.1
方向 = 0.0
所有者 = Joe
座席数 = 4
座っている人の数 = 2

3.2 拡張したクラスのコンストラクタ Edit

3.2.1 コンストラクタの順序の依存性 Edit

練習問題 3.2 Edit

class X {
    protected int xMask = 0x00ff;
    protected int fullMask;
    
    {
        printMask(0);
    }
    
    public X() {
        printMask(1);
        fullMask = xMask;
        printMask(2);
    }
    
    public int mask(int orig) {
        return orig & fullMask;
    }
    
    void printMask(int step) {
        System.out.println(step + " " 
            + "xMask = 0x" + Integer.toHexString(xMask) + " "
            + "fullMask = 0x" + Integer.toHexString(fullMask));
    }
}

class Y extends X {
    protected int yMask = 0xff00;
    
    {
        printMask(3);
    }
    
    public Y() {
        printMask(4);
        fullMask |= yMask;
        printMask(5);
    }
    
    void printMask(int step) {
        System.out.println(step + " "
                + "xMask = 0x" + Integer.toHexString(xMask) + " "
                + "yMask = 0x" + Integer.toHexString(yMask) + " "
                + "fullMask = 0x" + Integer.toHexString(fullMask));
    }
}

public class Mask {

    public static void main(String[] args) {
        Y mask = new Y();
    }
}

出力結果は以下の通り。

0 xMask = 0xff yMask = 0x0 fullMask = 0x0
1 xMask = 0xff yMask = 0x0 fullMask = 0x0
2 xMask = 0xff yMask = 0x0 fullMask = 0xff
3 xMask = 0xff yMask = 0xff00 fullMask = 0xff
4 xMask = 0xff yMask = 0xff00 fullMask = 0xff
5 xMask = 0xff yMask = 0xff00 fullMask = 0xffff

練習問題 3.3 Edit

例えば、xMaskをメソッドにして、サブクラスでオーバーライドしてやれば可能かと。

3.3メンバーの継承と再定義 Edit

3.3.1 オーバーライド Edit

3.3.2 フィールドの隠蔽 Edit

3.3.3 継承されたメンバーへのアクセス Edit

3.3.4 アクセス制限とオーバーライド Edit

3.3.5 staticメンバーの隠蔽 Edit

3.3.6 super予約語 Edit

3.4 型の互換性と変換 Edit

3.4.1 互換性 Edit

3.4.2 明示的な型キャスト Edit

3.4.3 型の検査 Edit

3.5 protectedが本当に意味すること Edit

3.6 メソッドとクラスのfinal宣言 Edit

練習問題 3.4 Edit

練習問題3.1の例では、
乗り物の座席数は変わらないと見て、
seatingCapacityをfinalにするのが適切であると思う。

3.7 抽象クラスと抽象メソッド Edit

練習問題 3.5 Edit

abstract class Benchmark {
    abstract void benchmark();
    
    public final long repeat(int count) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < count; i++)
            benchmark();
        return System.currentTimeMillis() - start;
    }
}

public class MethodBenchmark extends Benchmark {
    int loopCount;
    
    void benchmark() {
        for (int i = 0; i < loopCount; i++) ;
    }
    
    public static void main(String[] args) {
        int count = Integer.parseInt(args[0]);
         
        MethodBenchmark benchmark = new MethodBenchmark();
        benchmark.loopCount = Integer.parseInt(args[1]);
        long time = benchmark.repeat(count);
        System.out.println(
                benchmark.loopCount + " loop " 
                + count + " methods in " + time + " milliseconds");
    }
}

実行例

java MethodBenchmark 1000 10000

出力例

10000 loop 1000 methods in 16 milliseconds

練習問題 3.6 Edit

Vehicle書くと長くなるので、ExtendVehicleに拡張して記述した。

abstract class EnergySource {
    abstract boolean empty();
}

class GasTank extends EnergySource {
    public double fuel = 100.0;
    
    boolean empty() {
        return fuel <= 0;
    }
}

class Battery extends EnergySource {
    public double voltage = 10.0;
    boolean empty() {
        return voltage <= 0;
    }
}

public class ExtendVehicle extends Vehicle {
    EnergySource energy;
    
    ExtendVehicle(EnergySource energySource) {
        energy = energySource;
    }
    
    void start() {
        if (energy.empty()) {
            System.out.println("燃料が空です。");
        } else {
            System.out.println("動作開始します。");
        }
       
    }
    
    public static void main(String[] args) {
        ExtendVehicle gasVehicle = new ExtendVehicle(new GasTank());
        gasVehicle.start();
        
        ExtendVehicle batteryVehicle = new ExtendVehicle(new Battery());
        batteryVehicle.start();
    }
}

出力結果は以下の通り

動作開始します。
動作開始します。

3.8 Objectクラス Edit

Objectクラスは、クラス階層のルートクラス。
あらゆるクラスは、Objectを拡張している。

Objectクラスは、ユーティリティメソッドとスレッドをサポートするメソッドを持つ。
以下は、Objectクラスのメソッド

  • equals
    オブジェクトが等しいかどうか
  • hashCode
    ハッシュ値を返す
  • clone
    複製する
  • getClass
    クラス型を返す
  • finalize
    ガーベジコレクション時の終了処理
  • toString
    オブジェクトの文字列表現を返す

異なる等価判定をしたいなら、hashCodeとequalsはオーバーライドすべき。

同一性は、参照の等価をあらわす。
参照が等しいかどうか、つまり、==がtrueかどうか。

同値性は、オブジェクトの中身が等しいかどうか。
equalsが等しいかどうか。

練習問題 3.7 Edit

public class Vehicle {
    private double velocity;  // 現在のスピード
    private double angle;     // 現在の方向
    private String owner;      // 所有者
    
    private long idNum;
    private static long nextID = 0;
    
    public Vehicle() {
        idNum = nextID++;
    }
    
    public Vehicle(String firstOwner) {
        this();
        owner = firstOwner;
    }
    
    public boolean equals(Object obj) {
        if (!(obj instanceof Vehicle)) { return false; }
        
        Vehicle vehicle = (Vehicle)obj;
        return (velocity == vehicle.velocity)
            && (angle == vehicle.angle)
            && (owner == vehicle.owner);
    }
    
    public int hashCode() {
        return (int)(velocity + angle + owner.hashCode()); 
    }
    
    public static void main(String[] args) {
        Vehicle bicycle = new Vehicle("Mike");
        bicycle.velocity = 0.1;
        bicycle.angle = 0;
        
        Vehicle car = new Vehicle("Mike");
        car.velocity = 100.0;
        car.angle = 0;
        
        System.out.println("bicycle.equals(car) = " + bicycle.equals(car));
        System.out.println("bicycle.hashcode() = " + bicycle.hashCode());
        System.out.println("car.hashcode() = " + car.hashCode());
    }
}

実行結果は以下のとおり

bicycle.equals(car) = false
bicycle.hashcode() = 2398230
car.hashcode() = 2398330

3.9 オブジェクトの複製 Edit

cloneメソッドはオブジェクトの状態の複製を行う。

3.9.1 複製に関する戦略 Edit

cloneメソッドを書く上で重要な点。

  • Cloneableインターフェスを実装すること
  • Objectのcloneメソッドは単純な複製であること。必要があるならをオーバーライドすること。
  • cloneメソッドを呼ぶべきでないときは、例外を出すこと。

cloneメソッドをどうするかはいくつかの方法がある。

  • cloneをサポートする
  • 条件付でサポートする
  • サブクラスにまかせる
  • cloneを禁止する。例外を投げる。

一番簡単なcloneの実装方法は、Cloneableインターフェスを実装するように、cloneをpublicにして、Object.cloneを呼び出すこと。

cloneを実装したクラスのサブクラスは、Cloneableを実装していることになるけれども、複製できないとすることもできる。

3.9.2 正しい複製 Edit

普通の複製はできるが、
オブジェクトが、配列への参照や、オブジェクトへの参照を持っているときに問題が起こる。

3.9.3 浅い複製と深い複製 Edit

浅い(shallow)複製と、深い(deep)複製がある。

浅い複製は単純な値のコピー。
深い複製は、参照先も複製する。

練習問題 3.8 Edit

Vehicle.java

public class Vehicle implements Cloneable {
    private double velocity;  // 現在のスピード
    private double angle;     // 現在の方向
    private String owner;      // 所有者
    
    private long idNum;
    private static long nextID = 0;
    
    public Vehicle() {
        idNum = nextID++;
    }
    
    public Vehicle(String firstOwner) {
        this();
        owner = firstOwner;
    }
    
    public void setVelocity(double value) { velocity = value; }
    public double getVelocity() { return velocity; }
    
    public void setAngle(double value) { angle = value; }
    public double getAngle() { return angle; }
    
    public String toString() {
        return "ID = " + idNum + "\n" 
            + "スピード = " + velocity + "\n"  
            +"方向 = "+ angle + "\n"
            +"所有者 = "+ owner;
    }

    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e.toString());
        }
    }
}

PassengerVehicle.java

public class PassengerVehicle extends Vehicle {
    private int seatingCapacity;    // 座席数
    private int seatedPerson;       // 座っている人の数
    
    public void setSeatingCapacity(int value) { seatingCapacity = value; }
    public int getSeatingCapacity() { return seatingCapacity; }

    public void setSeatedPerson(int value) { seatedPerson = value; }
    public int getSeatedPerson() { return seatedPerson; }
    
    public PassengerVehicle(String firstOwner) {
        super(firstOwner);
    } 
    
    public String toString() {
        return super.toString() + "\n" 
            + "座席数 = " + seatingCapacity + "\n"  
            + "座っている人の数 = "+ seatedPerson;
        
    }
    
    public static void main(String[] args) {
        PassengerVehicle bicycle = new PassengerVehicle("Mike");
        bicycle.setVelocity(0.1);
        bicycle.setSeatingCapacity(1);
        bicycle.setSeatedPerson(1);
        System.out.println(bicycle + "\n");
        
        
        PassengerVehicle copy = (PassengerVehicle) bicycle.clone();
        System.out.println(copy + "\n");
    }
}

実行結果は以下の通り

ID = 0
スピード = 0.1
方向 = 0.0
所有者 = Mike
座席数 = 1
座っている人の数 = 1

ID = 0
スピード = 0.1
方向 = 0.0
所有者 = Mike
座席数 = 1
座っている人の数 = 1

練習問題 3.9 Edit

public class Garage implements Cloneable{
    private Vehicle[] buffer;
    private int top;
    
    public Garage(int maxVehicle) {
        buffer = new Vehicle[maxVehicle];
        top = -1;
    }
    
    public void push(Vehicle value) {
        ++top;
        buffer[top] = value;
    }
    
    public Object clone() {
        try {
            Garage obj = (Garage) super.clone();
            obj.buffer = new Vehicle[buffer.length];
            for (int i = 0; i <= top; i++) {
                obj.buffer[i] = (Vehicle) buffer[i].clone();
            }
            return obj;
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e.toString());
        }
    }
        
    public static void main(String[] args) {
        Garage garage = new Garage(16);
        
        Vehicle bicycle = new Vehicle("Ken");
        bicycle.setVelocity(0.1);
        garage.push(bicycle);
        
        Vehicle car = new Vehicle("Mike");
        car.setVelocity(100);
        garage.push(car);
        
        Garage newGarage = (Garage)garage.clone();
        
        for (int i = 0; i <= newGarage.top; i++) {
            System.out.println(newGarage.buffer[i]);
        } 
    }
}

実行結果は以下の通り

ID = 0
スピード = 0.1
方向 = 0.0
所有者 = Ken
ID = 1
スピード = 100.0
方向 = 0.0
所有者 = Mike

練習問題 3.10 Edit

public class LinkedList implements Cloneable {
    private Vehicle data;
    private LinkedList next;

    public String toString() {
        return "data:\n" + data;
    }
    
    public void show() {
        System.out.println(this);
        if (next != null)
            next.show();
    }    
    
    public Object clone() {
        try {
            LinkedList list = (LinkedList) super.clone();
            if (next != null) {
                list.next = (LinkedList) next.clone(); 
            }
            return list; 
            
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e.toString());
        }
    }   
    public static void main(String[] args) {
        LinkedList item1 = new LinkedList();
        LinkedList item2 = new LinkedList();
        LinkedList item3 = new LinkedList();
        
        Vehicle bicycle = new Vehicle("Ken");
        bicycle.setVelocity(0.1);
        item1.data = bicycle;
        item1.next = item2;
        
        Vehicle bike = new Vehicle("Joe");
        bike.setVelocity(50);
        item2.data = bike;
        item2.next = item3;
        
        Vehicle car = new Vehicle("Mike");
        car.setVelocity(100);
        item3.data = car;
        
        item1.show();
        System.out.println("\n");
        
        // 複製
        LinkedList newList = (LinkedList) item1.clone();
        
        // リストの順番入れ替えてみる
        item1.next = item3;
        item3.next = item2;
        item2.next = null;
        
        // 速度を変更してみる
        newList.data.setVelocity(0);
        
        // それぞれ出力
        item1.show();
        System.out.println("\n");
        newList.show();
    }
}

以下は実行結果。

data:
ID = 0
スピード = 0.1
方向 = 0.0
所有者 = Ken
data:
ID = 1
スピード = 50.0
方向 = 0.0
所有者 = Joe
data:
ID = 2
スピード = 100.0
方向 = 0.0
所有者 = Mike


data:
ID = 0
スピード = 0.0
方向 = 0.0
所有者 = Ken
data:
ID = 2
スピード = 100.0
方向 = 0.0
所有者 = Mike
data:
ID = 1
スピード = 50.0
方向 = 0.0
所有者 = Joe


data:
ID = 0
スピード = 0.0
方向 = 0.0
所有者 = Ken
data:
ID = 1
スピード = 50.0
方向 = 0.0
所有者 = Joe
data:
ID = 2
スピード = 100.0
方向 = 0.0
所有者 = Mike

3.10 クラスの拡張: どのように、そしていつ Edit

IsA。元のクラスと同じ種類のオブジェクトを生成する。

HasA。他のオブジェクトの参照を持つ。

Point をextends したPixelはIsA。

円はPointのIsAではない。中心点はもつ、つまり点のHasA。

3.11 拡張されるクラスの設計 Edit




Front page   Edit Freeze Diff Backup Upload Copy Rename Reload   New Pages Search Recent changes   Help   RSS of recent changes
Last-modified: 2005-06-29 Wed 12:35:33 JST (5324d)