[Work/Class/Java with Processing/3_ProcessingClass]

アート系学生のためのJAVA with Processing - その3-4 インナークラスと外部クラスの違い,その活用

インナークラス

今まで作ってきたクラスは全て「外部クラス」である.それとは別にメインのクラスとなるメインスケッチ内にクラスを作ることができる.これを「内部クラス」「インナークラス」「メンバクラス」と呼ぶ.

メインスケッチ以外にも,クラスの中にクラスを作ることはでき,やはり内部クラス,インナークラス,メンバクラスと呼称する.

インナークラスの存在意義は,そのインナークラスは外側のクラスの「メンバ」である,とされることが大きい.

すなわち「メインスケッチ(もしくはクラス内)のインスタンス変数などのデータと密接に関連付いているため,本来ならメインスケッチ内(もしくはクラス内)に変数や関数として定義するべきだが,それらをまとめてクラスとしてパッケージングしたほうがわかりやすい」という場合にインナークラスを作る.

そのためインナークラスは,他のクラスからは呼び出すことができない.

今は覚えなくても良い: 実は関数内部にも内部クラスを作ることができ,これを「ローカルクラス」と呼称する.ローカルクラスは関数内部からしか呼ぶことができない.クロージャ的に使う(?)

コード

以下はBallの色要素をBallクラスのインナークラスとして独立させた例である.

// Processing_InnerClass.pde
BallContainsInnerClass ballArray[];

void setup(){
  size(640, 480);
  ballArray = new BallContainsInnerClass [20];
  for(int i=0; i<20; i++)
   ballArray[i] = new BallContainsInnerClass(width, height); 
}

void draw(){
  fill(255, 255, 255, 60);
  rect(0, 0, width, height);
  for(int i=0; i<20; i++){
    ballArray[i].updateParameters();
    ballArray[i].drawBall();
  }
}
// BallContainsInnerClass.pde
class BallContainsInnerClass{
  //色情報をインナークラスとして独立させたBallクラス
  int windowWidth, windowHeight;
  int xPos, yPos;
  int xDirection, yDirection;
  
  ColorInformation colorInformation; //インナークラスのインスタンスを保持する変数
  
  BallContainsInnerClass(int windowWidth, int windowHeight){
    // Constructor コンストラクタ
    this.windowWidth = windowWidth;
    this.windowHeight = windowHeight;
    
    // Make Instance of the ColorInformation Inner Class
    // インナークラスのインスタンス化
    colorInformation = new ColorInformation();
    
    xPos = (int)random(windowWidth);
    yPos = (int)random(windowWidth);
    if(random(100) > 50)
      xDirection = 1;
    else
      xDirection = -1;

    if(random(100) > 50)
      yDirection = 1;
    else
      yDirection = -1;
  }
  
  void updateParameters(){
    colorInformation.updateColor();
    
    xPos += xDirection * 10;
    yPos += yDirection * 10;
    
    if(xPos > windowWidth)
      xDirection = -1;
    else if(xPos < 0)
      xDirection = 1;
      
    if(yPos > windowHeight)
      yDirection = -1;
    else if(yPos < 0)
      yDirection = 1;
  }
  
  void drawBall(){
    colorInformation.fillObject(); 
    ellipse(xPos, yPos, 20, 20);
  }
  
  private class ColorInformation{
   // 色情報を独立させたインナークラス
   // Ballクラスからだけインスタンス化できればいいので,privateキーワードをつける
   int rColor, gColor, bColor;
   int colorDirection;
   
   private ColorInformation(){
     // Constructor of Inner Class
     // 色情報に関わるインナークラスのコンストラクタ
     // privateクラスのコンストラクタなのでprivateキーワードをつける
     rColor = (int)random(100) + 155;
     gColor = (int)random(100) + 155;
     bColor = (int)random(100) + 155;
     
     if(random(100) > 50)
       colorDirection = 1;
     else
       colorDirection = -1;
   }
   
   void updateColor(){
    //インナークラスの外から触るのはこの関数とfillObject()関数のみ
     if(colorDirection > 0)
       lightenColor();
     else if(colorDirection < 0)
       darkenColor();
       
     if(hasMaxColor())
       colorDirection = -1;
     else if(hasMinColor())
       colorDirection = 1;
   }

   void fillObject(){
    fill(rColor, gColor, bColor); 
   }
   
   private void lightenColor(){
     // privateキーワードをつけて,クラス内部からしか呼び出せない関数にする
     rColor += 10;
     if(rColor > 255)
       rColor = 255;
       
     gColor += 10;
     if(gColor > 255)
       gColor = 255;
       
     bColor += 10;
     if(bColor > 255)
       bColor = 255;
   }
   
   private void darkenColor(){
     // 同じくprivateキーワード
     rColor -= 10;
     if(rColor < 0)
       rColor = 0;
     
     gColor -= 10;
     if(gColor < 0)
       gColor = 0;
     
     bColor -= 10;
     if(bColor < 0)
       bColor = 0;
   }
   
   private void setColorDirection(int colorDirection){
    // 同じくprivate
    this.colorDirection = colorDirection; 
   }
   
   private boolean hasMaxColor(){
    // 同じくprivate
    if(rColor >= 255 || bColor >= 255 || gColor >= 255)
      return true;
    else
      return false;
   }
   
   private boolean hasMinColor(){
     // 同じくprivate
     if(rColor <= 0 || bColor <= 0 || gColor <= 0)
       return true;
     else
       return false;
   }   
  }
}

応用課題

座標情報とその更新も,別のインナークラスを作ってパッケージ化してしまおう.