継承におけるコンストラクタの取り扱い

継承におけるコンストラクタの取り扱いについて詳しく見ていく前に、コンストラクタはどのようなものであったか復習してみる。

上記に加え、継承におけるコンストラクタは以下のようになる。

では、実際にプログラムを用いて順に説明を行う。

クラスの継承によってコンストラクタは継承されない

前回の継承のプログラムを用いて、説明を行う。

これが前回のプログラムである。
//スーパークラスShape(Shape.java)
public class Shape{
   
   //図形の色を入れるためのフィールド
   private String color;

   //図形の色をセットするためのメソッド
   public void setColor( String color ){
      this.color = color;
   }

   //図形の色を返すメソッド
   public String getColor(){
      return color;
   }

}
//長方形クラス(Rectangle.java):
public class Rectangle extends Shape{

   //図形の名前(Rectangle)を格納したクラス変数
   public static final String NAME = "Rectangle";

   //コンストラクタ
   public Rectangle( String color ){
      setColor( color );
   }

}
//円クラス(Circle.java):
public class Circle extends Shape{
   
   //図形の名前(Circle)を格納したクラス変数
   public static final String NAME = "Circle";

   //コンストラクタ
   public Circle( String color ){
      setColor( color );
   }

}	
//ShapeTestクラス(ShapeTest.java):
public class ShapeTest{
   public static void main( String args[] ){

      //インスタンスを生成
      Rectangle shape1 = new Rectangle( "Red" );
      Circle shape2 = new Circle( "Yellow" );

      //各インスタンスの色の表示
      System.out.println( shape1.name + "'s color : " + shape1.getColor() );
      System.out.println( shape2.name + "'s color : " + shape2.getColor() );

   }
}

RectangleクラスとCircleクラスのコンストラクタに同じ部分があるので、スーパークラスであるShapeクラスにまとめてコンストラクタを定義してみる。

//スーパークラスShape(Shape.java)
public class Shape{
   
   //図形の色を入れるためのフィールド
   private String color;

   //コンストラクタ
   public Shape( String color ){
      setColor( color );
   }
   

   //図形の色をセットするためのメソッド
   public void setColor( String color ){
      this.color = color;
   }

   //図形の色を返すメソッド
   public String getColor(){
      return color;
   }

}
//長方形クラス(Rectangle.java):
public class Rectangle extends Shape{

   //図形の名前(Rectangle)を格納したクラス変数
   public static final String NAME = "Rectangle";

   //コンストラクタ
   public Shape( String color ){
      setColor( color );
   }

}
//円クラス(Circle.java):
public class Circle extends Shape{
   
   //図形の名前(Circle)を格納したクラス変数
   public static final String NAME = "Circle";

   //コンストラクタ
   public Shape( String color ){
      setColor( color );
   }

}	
//ShapeTestクラス(ShapeTest.java):
public class ShapeTest{
   public static void main( String args[] ){

      //インスタンスを生成
      Rectangle shape1 = new Rectangle( "Red" );
      Circle shape2 = new Circle( "Yellow" );

      //各インスタンスの色の表示
      System.out.println( shape1.name + "'s color : " + shape1.getColor() );
      System.out.println( shape2.name + "'s color : " + shape2.getColor() );

   }
}

このソースをコンパイルすると、以下のようなコンパイルエラーが起こる。


[eiko@eiko Java]$ javac ShapeTest.java
ShapeTest.java:25: Shape の Shape(java.lang.String) は () に適用できません。
class Rectangle extends Shape{
^
ShapeTest.java:33: Shape の Shape(java.lang.String) は () に適用できません。
class Circle extends Shape{
^
ShapeTest.java:45: シンボルを解決できません。
シンボル: コンストラクタ Rectangle (java.lang.String)
場所    : Rectangle の クラス
      Rectangle shape1 = new Rectangle( "Red" );
                         ^
ShapeTest.java:46: シンボルを解決できません。
シンボル: コンストラクタ Circle (java.lang.String)
場所    : Circle の クラス
      Circle shape2 = new Circle( "Yellow" );
                      ^
エラー 4 個

これは、継承によってスーパークラスのコンストラクタはサブクラスに継承されないからである。

サブクラスでのコンストラクタ定義

上のソースでコンストラクタは継承されないので、継承を行ってもサブクラスでコンストラクタを定義する必要があることがわかった。
そこで、下のソースではスーパークラスだけでなく、サブクラスにもコンストラクタの定義を行った。

//スーパークラスShape(Shape.java)
public class Shape{
   
   //図形の色を入れるためのフィールド
   private String color;

   //コンストラクタ
   public Shape( String color ){
      setColor( color );
   }

   //図形の色をセットするためのメソッド
   public void setColor( String color ){
      this.color = color;
   }

   //図形の色を返すメソッド
   public String getColor(){
      return color;
   }

}
//長方形クラス(Rectangle.java):
public class Rectangle extends Shape{

   //図形の名前(Rectangle)を格納したクラス変数
   public static final String NAME = "Rectangle";

   
   //コンストラクタ
   public Rectangle( String color ){
      setColor( color );
   }
   

}
//円クラス(Circle.java):
public class Circle extends Shape{
   
   //図形の名前(Circle)を格納したクラス変数
   public static final String NAME = "Circle";

   
   //コンストラクタ
   public Circle( String color ){
      setColor( color );
   }
   

}	
//ShapeTestクラス(ShapeTest.java):
public class ShapeTest{
   public static void main( String args[] ){

      //インスタンスを生成
      Rectangle shape1 = new Rectangle( "Red" );
      Circle shape2 = new Circle( "Yellow" );

      //各インスタンスの色の表示
      System.out.println( shape1.name + "'s color : " + shape1.getColor() );
      System.out.println( shape2.name + "'s color : " + shape2.getColor() );

   }
}

このソースをコンパイルすると、今度は以下のように別の部分でコンパイルエラーが起こる。


[eiko@eiko Java]$ javac ShapeTest.java
ShapeTest.java:31: Shape の Shape(java.lang.String) は () に適用できません。
   public Rectangle( String color ){
                                   ^
ShapeTest.java:45: Shape の Shape(java.lang.String) は () に適用できません。
   public Circle( String color ){
                                ^
エラー 2 個

継承を行った場合、コンストラクタの定義の最初でスーパークラスのコンストラクタ又は自分のクラスの別のコンストラクタを呼び出さない場合、暗黙的にスーパークラスの引数なしコンストラクタを呼び出す形になるという話をはじめにした。
このソースの場合、スーパークラスで引数つきのコンストラクタを定義した時点で、デフォルトのコンストラクタが作られなくなるため、スーパークラスに引数なしのコンストラクタは存在しない。
しかし、サブクラスのコンストラクタが暗黙的にスーパークラスの引数なしコンストラクタを呼び出しているため、コンパイルエラーが起こっているのである。

では、どのようにこのコンパイルエラーを回避すればよいのか?例を以下に示す。

エラー回避法1(スーパークラスに引数なしコンストラクタを定義する)

一番簡単なエラー回避の方法として、スーパークラスShapeに引数なしのコンストラクタを以下のように定義する方法がある。
これにより、スーパークラスShapeで引数つきコンストラクタを定義したことによって、デフォルトコンストラクタが作られなくなった分を補うことができる。

//スーパークラスShape(Shape.java)
public class Shape{
   
   //図形の色を入れるためのフィールド
   private String color;

   
   //コンストラクタ(引数なし)
   public Shape(){
   }
   

   //コンストラクタ(引数あり)
   public Shape( String color ){
      setColor( color );
   }

   //図形の色をセットするためのメソッド
   public void setColor( String color ){
      this.color = color;
   }

   //図形の色を返すメソッド
   public String getColor(){
      return color;
   }

}
//長方形クラス(Rectangle.java):
public class Rectangle extends Shape{

   //図形の名前(Rectangle)を格納したクラス変数
   public static final String NAME = "Rectangle";

   //コンストラクタ
   public Rectangle( String color ){
      setColor( color );
   }


}
//円クラス(Circle.java):
public class Circle extends Shape{
   
   //図形の名前(Circle)を格納したクラス変数
   public static final String NAME = "Circle";

   //コンストラクタ
   public Circle( String color ){
      setColor( color );
   }


}	
//ShapeTestクラス(ShapeTest.java):
public class ShapeTest{
   public static void main( String args[] ){

      //インスタンスを生成
      Rectangle shape1 = new Rectangle( "Red" );
      Circle shape2 = new Circle( "Yellow" );

      //各インスタンスの色の表示
      System.out.println( shape1.name + "'s color : " + shape1.getColor() );
      System.out.println( shape2.name + "'s color : " + shape2.getColor() );

   }
}

このように、クラスを定義する際に、引数なしのコンストラクタをいつも定義する癖をつけておくことで、今回のようなエラーを回避することができる。

エラー回避法2(スーパークラスの引数つきコンストラクタをサブクラスのコンストラクタ定義で呼び出す)

今回のソースの場合、colorに引数を代入するという動作は同じなので、以下のようにスーパークラスShapeの引数つきコンストラクタを明示的に呼び出せば、スーパークラスの引数なしコンストラクタを呼び出すということがなくなる。
こうすることで、サブクラスでcolorに値を代入する必要がなくなるので、setColorメソッドを定義する必要がなくなるのである。

//スーパークラスShape(Shape.java)
public class Shape{
   
   //図形の色を入れるためのフィールド
   private String color;

   //コンストラクタ(引数あり)
   public Shape( String color ){
      this.color = color;
   }

   //図形の色をセットするためのメソッド
   public void setColor( String color ){
      this.color = color;
   }

   //図形の色を返すメソッド
   public String getColor(){
      return color;
   }

}
//長方形クラス(Rectangle.java):
public class Rectangle extends Shape{

   //図形の名前(Rectangle)を格納したクラス変数
   public static final String NAME = "Rectangle";

   //コンストラクタ
   public Rectangle( String color ){
      super( color );
   }


}
//円クラス(Circle.java):
public class Circle extends Shape{
   
   //図形の名前(Circle)を格納したクラス変数
   public static final String NAME = "Circle";

   //コンストラクタ
   public Circle( String color ){
      super( color );
   }


}	
//ShapeTestクラス(ShapeTest.java):
public class ShapeTest{
   public static void main( String args[] ){

      //インスタンスを生成
      Rectangle shape1 = new Rectangle( "Red" );
      Circle shape2 = new Circle( "Yellow" );

      //各インスタンスの色の表示
      System.out.println( shape1.name + "'s color : " + shape1.getColor() );
      System.out.println( shape2.name + "'s color : " + shape2.getColor() );

   }
}