67475


以下のプログラムで、Circleクラスを自作で作りました（オブジェクト指向）  
最初にCircleクラスの配列circlesを用意して（数は20）、その下のfor文でCircleクラスのインスタンスをn回生成しているのですが、ここでなぜ
`public Sample83(){}` と、わざわざSample83のコンストラクタをする必要があるのでしょうか？

[code]

    int n = 20;
       Circle[] circles = new Circle[n];
    
       for (int i = 0; i<n;++i)
          {circles[i]  = new Circle(Color.BLUE, 30+40*i, 100, 15);
          }
    
[/code]

配列circlesを作ったら、そのまま続けてfor文を書けばいいと思うのですが、この様にするとエラーが出てしまいます。

元のコードは以下の通りです。

[code]

    import java.awt.*;
      import javax.swing.*;
    
      public class Sample83 extends JPanel {
       int n = 20;
       Circle[] circles = new Circle[n];
    
       public Sample83() {
          for (int i = 0; i<n;++i)
          {circles[i]  = new Circle(Color.BLUE, 30+40*i, 100, 15);
          }
    
        }
    
        public void paintComponent(Graphics g) {
          for (int i = 0; i < n; ++i) {circles[i].draw(g); }
        }
        public static void main(String[] args) {
          JFrame app = new JFrame();
          app.add(new Sample83());
          app.setSize(880, 300);
          app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          app.setVisible(true);
        }
        static class Circle {
          Color col;
          int xpos, ypos, rad;
          public Circle(Color c, int x, int y, int r) {
            col = c; xpos = x; ypos = y; rad = r;
          }
          public void draw(Graphics g) {
            g.setColor(col);
            g.fillOval(xpos-rad, ypos-rad, rad*2, rad*2);
          }
        }
      }
    
[/code]

2つ目のプログラムを追記します。このプログラムでは「文」である  
”Circle c1 = new Circle(new Color(200,200,0), 150, 90, 50);”  
などが、クラスSample82直下に記入できています。1つ目のプログラムでは、直下ではなく、Sample83のコンストラクタを実行する必要がありますが（「文」なのでブロック{}の中に書く必要があると聞きました)が、なぜ2つ目のプログラムはクラスの定義に直接かけるのでしょうか？

[code]

    import java.awt.*;
    import javax.swing.*;
    
    public class Sample82 extends JPanel {
      Circle c1 = new Circle(new Color(200,200,0), 150, 90, 50);
      Circle c2 = new Circle(new Color(0,200,200), 150, 90, 40);
      Circle c3 = new Circle(new Color(200,0,200), 150, 90, 30);
      Rect r1 = new Rect(Color.GREEN, 270, 180, 80, 50);
      Triangle t1 = new Triangle(Color.YELLOW, 50, 220, 250, 180, 150, 150);
    
      public void paintComponent(Graphics g) {
        c1.draw(g); c2.draw(g);c3.draw(g); r1.draw(g); t1.draw(g);
      }
      public static void main(String[] args) {
        JFrame app = new JFrame();
        app.add(new Sample82());
        app.setSize(400, 300);
        app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        app.setVisible(true);
      }
      static class Circle {
        Color col;
        int xpos, ypos, rad;
        public Circle(Color c, int x, int y, int r) {
          col = c; xpos = x; ypos = y; rad = r;
        }
        public void draw(Graphics g) {
          g.setColor(col);
          g.fillOval(xpos-rad, ypos-rad, rad*2, rad*2);
        }
      }
      static class Rect {
        Color col;
        int xpos, ypos, width, height;
        public Rect(Color c, int x, int y, int w, int h) {
          col = c; xpos = x; ypos = y; width = w; height = h;
        }
        public void draw(Graphics g) {
          g.setColor(col);
          g.fillRect(xpos-width/2, ypos-height/2, width, height);
        }
      }
      static class Triangle {
        Color col;
        int[] xa, ya;
        public Triangle(Color c, int x0, int y0, int x1, int y1,
                        int x2, int y2) {
          col = c; xa = new int[]{x0,x1,x2}; ya = new int[]{y0,y1,y2};
        }
        public void draw(Graphics g) {
          g.setColor(col); g.fillPolygon(xa, ya, 3);
        }
      }
    }
    
[/code]

・追加で質問です。  
下の様に変数宣言の2行を、Sample83のコンストラクタ{}の中に書いて実行したところエラーが出ました。どうやらこの2行は外に書く必要があるみたいですが、なぜでしょうか？

エラー内容：  
シンボルを見つけられません（シンボル：n,circles）

[code]

    public Sample83() {
        int n = 20;
        Circle[] circles = new Circle[n];
          for (int i = 0; i<n;++i)
          {circles[i]  = new Circle(Color.BLUE, 30+40*i, 100, 15);
          }
    }
    
[/code]


CC BY-SA 4.0
2020-06-10T00:36:50.047
67473
2020-06-12T07:56:21.070
2020-06-12T07:56:21.070
3060
39688
question
java
インスタンス作成時にコンストラクタ実行が必要な理由は？


### _なぜ public Sample83(){} と、わざわざSample83のコンストラクタをする必要がある_

Javaにおいては、クラスの直下には「文」を書くことができないためです。

Javaにおいて、

  * `int n = 20;`
  * `Circle[] circles = new Circle[n];`

などは、変数宣言(もう少し正確に言うと、今回の場合「インスタンスフィールドの宣言」)であり、このような「宣言」は`class`の直下に書けます。(これらは「代入文」ではありません。)

それに対して、

  * `for (int i = 0; i<n;++i) {...}`
  * `circles[i] = new Circle(Color.BLUE, 30+40*i, 100, 15);`

等々は、「文」(2つ目はいわゆる「代入文」、正確に言うと『代入演算子のある「式文」』)であり、Javaの場合、「文」を置くことができるのは、メソッド本体などの`{`と`}`で囲まれた「ブロック」の中だけで、クラス定義の本体部分に直接記載することは出来ません。

[8.1. Class
Declarations](https://docs.oracle.com/javase/specs/jls/se14/html/jls-8.html#jls-8.1)

[14.5.
Statements](https://docs.oracle.com/javase/specs/jls/se14/html/jls-14.html#jls-14.5)

上記の公式ドキュメントの記載はとても見やすいとは言えないですし、内容や公式の用語を丸暗記しなければJavaのプログラミングができないと言うものでもないですが、

**「文」と「宣言」の違いが把握できていないと、今後確実に苦労する**

ことになるため、その辺りの違い(特に「代入文」と「初期値を指定した変数宣言」の違い、同じ変数宣言でもフィールドの宣言とローカル変数の宣言の違い)は、強く意識されていった方が良いでしょう。

* * *

### 2つ目のプログラムについて

この回答の中身をまだ十分ご理解いただいていないように思います。

繰り返しになりますが、

  * `Circle[] circles = new Circle[n];`は変数宣言
  * `circles[i] = new Circle(Color.BLUE, 30+40*i, 100, 15);`は代入文

です。

あなたの引用された、

`Circle c1 = new Circle(new Color(200,200,0), 150, 90, 50);`

は、変数宣言ですので、『「文」である』と言う判断が誤っています。

2つ目のプログラムで、以下は全て「文」ではなく、変数宣言です。

[code]

    Circle c1 = new Circle(new Color(200,200,0), 150, 90, 50);
    Circle c2 = new Circle(new Color(0,200,200), 150, 90, 40);
    Circle c3 = new Circle(new Color(200,0,200), 150, 90, 30);
    Rect r1 = new Rect(Color.GREEN, 270, 180, 80, 50);
    Triangle t1 = new Triangle(Color.YELLOW, 50, 220, 250, 180, 150, 150);
    
[/code]

* * *

### 変数宣言と文

あまり厳密に公式ドキュメントの構文規則を引用してもわかりにくいだけなので、ざっくりまとめると、先頭(`public`だの`private`だのがあればその後)に「型」が記載されていれば、それは「宣言」になります。

[code]

    Circle[] circles = new Circle[n];
    --------
    ↑型
    
[/code]

ちょっとだけ一般化すると、

[code]

    型 変数名;
    
[/code]

または、

[code]

    型 変数名 = 初期化式;
    
[/code]

と言う構文になっているのが変数宣言です。

それに対して、先頭に「型」が記載されておらず、

[code]

    変数名 = 式;
    
[/code]

と言う構文になっているものは、「文」になります。  
(Javaでは、`変数名 = 式`全体も有効な「式」なので、後ろに`;`をつけて「文」として扱ったものを「式文」と言ったりします。)

初期化式を伴う変数宣言では、「代入」に相当する処理が行われると言うのは間違いありませんが、そのことと「宣言」であるか「文」であるかとは関係ありません。

* * *

### _追加で質問_ について

ここまでのこの回答では、「宣言」と「文」の区別に焦点を当てたかったので、細かい違いは無視して「変数宣言」として扱ってきたのですが、省略しすぎてわかりにくいものになってしまったようです。

「変数宣言」は、どこに書かれるかにより、大きく2つに分けられます。

  * クラス本体の直下に記載した場合、「フィールド宣言」になります

`static`のつかない「インスタンスフィールド」の宣言の場合、インスタンスが作られると同時にフィールド用の領域が確保され、インスタンスがガーベッジコレクションで回収されてなくなってしまうまで、存在し続けます。また、インスタンスフィールドはそのクラス内の全てのコンストラクタ、インスタンスメソッドで参照できます。

  * ブロック内に記載した場合、「ローカル変数の宣言」になります

ローカル変数として宣言された変数は、そのブロックの実行開始時に領域が確保され、ブロック自体の実行が終わると、その領域は開放されてしまいます。また、そのブロックの外から参照することは出来ません。

したがって、

[code]

        public Sample83() {
            //↓↓↓「このブロック」開始
            int n = 20;
            Circle[] circles = new Circle[n];
            for (int i = 0; i < n; ++i) {
                circles[i] = new Circle(Color.BLUE, 30 + 40 * i, 100, 15);
            }
            //↑↑↑「このブロック」終了
        }
    
[/code]

のように、`n`と`circles`をコンストラクタのブロック内でを「宣言」してしまうと、そのブロックの外では、そんな変数は無かったことにされてしまうわけです。

`paintComponent()`メソッドの中では`n`や`circles`を参照しようとしていますが、「そんな変数は無かったことにされて」いますから、そこでエラーになります。

このような場合、「宣言」は、必ずクラス直下に記載しないといけない、と言うことになります。ただし、初期化(初期値の代入)は必ずしも宣言と一緒に行う必要はありません。

[code]

        //初期化無しのフィールド宣言
        int n;
        Circle[] circles;
    
        public Sample83() {
            //宣言無しの代入だけ
            n = 20;
            circles = new Circle[n];
            for (int i = 0; i < n; ++i) {
                circles[i] = new Circle(Color.BLUE, 30 + 40 * i, 100, 15);
            }
        }
    
[/code]

(ただし、初期化し忘れによるバグを防ぐために、宣言と同時に初期化してしまう方が良い習慣とされています。)

と言うわけで、さっきまでより覚えないといけない項目が増えてしまいましたが、

  * フィールド宣言
  * ローカル変数の宣言
  * 代入文

は、区別できるようにして下さい。


CC BY-SA 4.0
2020-06-10T01:36:32.910
67475
2020-06-10T06:18:01.893
2020-06-10T06:18:01.893
13972
13972
67473
answer