ページ 1 / 1
Javaのインナークラスについて
Posted: 2011年6月25日(土) 18:24
by R1st
こんにちは、Javaの文法で分からないことが出てきましたので質問します。
コード:
public class Main
{
public void Start()
{
Player a1 = new Player();
Field w1 = new Field();
w1.life_paint();
}
public class Player
{
private int life = 100;
public int now()
{
if(life < 0) life = 0;
return life;
}
}
public class Field extend Graph
{
public void life_paint()
{
Graph.paint(a1.now());
}
}
}
この時、Fieldクラスは「a1.now()」を呼び出すことができません。
インナークラスで別のインナークラスのメソッドを呼び出したいのですが、どのように記述すればよいのでしょうか?
このコードは説明のために適当に書いたものなので、このコード自体の正誤はあまり気にしないでください。
Re: Javaのインナークラスについて
Posted: 2011年6月25日(土) 19:13
by bitter_fox
R1st さんが書きました:
この時、Fieldクラスは「a1.now()」を呼び出すことができません。
インナークラスで別のインナークラスのメソッドを呼び出したいのですが、どのように記述すればよいのでしょうか?
まず、Player a1がStartメソッドで定義されているのでStartメソッドを抜けると参照する(全てのメソッドから)ことが出来なくなりますので、Mainクラスのフィールドにしましょう。
また、インナークラスからアウタークラスのフィールドを参照するにはアウタークラスのフィールドがfinal修飾されている必要があります。
ですので、これらに従って書くと次のようになります。
コード:
public class Main
{
private final Player a1 = new Player();
public void start()
{
new Field().lifePaint();
}
public class Player
{
private int life = 10;
public int now()
{
if(life < 0) life = 0;
return life;
}
}
public class Field extends Graph
{
public void lifePaint()
{
Graph.paint(a1.now());
}
}
}
ただ、この設計だとダサいので僕だと次のようにします(インナークラスを絶対に使うのであれば・・・)
コード:
public class Main
{
public void start()
{
Player a1 = new Player();
new Field(a1).lifePaint();
}
public class Player // Mainクラス以外から呼ばれる必要がないのであればprivateにした方が良い
{
private int life = 10;
public int now()
{
if(life < 0) life = 0;
return life;
}
}
public class Field extends Graph // Mainクラス以外から呼ばれる必要がないのであればprivateにした方が良い
{
private Player a1;
public Field(Player a1)
{
this.a1 = a1;
}
public void lifePaint()
{
Graph.paint(a1.now());
}
}
}
(インナークラスである必要が全く無くなっているようにも感じますが・・・)
[hr]
[修正]
一つ目のコードのstartメソッドの戻り値の型が抜けていたので修正。
[追記]
Player a1をstartメソッドの外に出したくないのであれば次のようなことも可能です。
コード:
public class Main
{
public void start()
{
final Player a1 = new Player();
class Field extends Graph // 無名クラスにしてしまう手も・・・
{
public void lifePaint()
{
Graph.paint(a1.now());
}
}
new Field().lifePaint();
}
public class Player
{
private int life = 10;
public int now()
{
if(life < 0) life = 0;
return life;
}
}
}
[修正]三個目のコードのField.lifePaintメソッドでGraph.paintメソッドを呼び出してなかったので修正
Re: Javaのインナークラスについて
Posted: 2011年6月25日(土) 20:39
by R1st
bitter_foxさんありがとうございました!
無事解決することができました。
Re: Javaのインナークラスについて
Posted: 2011年6月27日(月) 02:39
by bitter_fox
bitter_fox さんが書きました:
まず、Player a1がStartメソッドで定義されているのでStartメソッドを抜けると参照する(全てのメソッドから)ことが出来なくなりますので、Mainクラスのフィールドにしましょう。
また、インナークラスからアウタークラスのフィールドを参照するにはアウタークラスのフィールドがfinal修飾されている必要があります。
もうすでにご覧になられてないかもしれませんが、今後のために一部間違っていたので修正します。
『インナークラスからアウタークラスのフィールドを参照するにはアウタークラスのフィールドがfinal修飾されている必要があります。』
と書きましたが、この制約を受けるのはメソッド内インナークラスのみで、final修飾されている必要があるのはメソッド内の変数のみになります。
ですので、クラス内インナークラスを用いる際は次のようにしても問題ありません。
コード:
public class Main
{
private Player a1; // クラス内インナークラスなのでfinalでなくて結構。
public void start()
{
a1 = new Player();
new Field().lifePaint();
}
public class Player
{
private int life = 10;
public int now()
{
if(life < 0) life = 0;
return life;
}
}
public class Field extends Graph
{
public void lifePaint()
{
Graph.paint(a1.now());
}
}
}
一方こちらはメソッド内インナークラスなのでfinal修飾されているという制約を受けます。
コード:
public class Main
{
// private Player a1 = new Player(); // ここに置く場合はメソッド内インナークラスでもfinal修飾されている必要はない。
public void start()
{
final Player a1 = new Player(); // final修飾されていないとアウト
class Field extends Graph
{
public void lifePaint()
{
Graph.paint(a1.now());
}
}
new Field().lifePaint();
}
public class Player
{
private int life = 10;
public int now()
{
if(life < 0) life = 0;
return life;
}
}
}
なぜこのような制約を受けるかというと次のようなケースが考えられるからでしょう。
コード:
/**
* 注:コンパイルは通りません
*/
public class Main
{
public Object obj; // startメソッドの変数xを参照するインナークラスのインスタンスをstartメソッドの外に出す。
public static void main(String[] args)
{
Main m = new Main();
m.start(); // startメソッドを呼び出してからprintメソッドを呼び出すが
m.print(); // startメソッドを既に抜けてるのでstartメソッドの変数xは参照できなくなってるので偉いことになってしまう。
// 仮にコンパイルが通り実行できたとしたらここで実行時エラーが発生してしまう。
}
public void start()
{
/*final*/ int x = 10; // final修飾されていないのでxはstartを抜けると参照できなくなってしまう。
class Inner
{
public String toString()
{
return Integer.toString(x);
}
}
obj = new Inner();
}
public void print()
{
System.out.println(obj); // startメソッド内のxを出力する(この時にstartメソッド内のxが残っていなければならない)
}
}
この様な書き方を許可してしまうと変数が参照できるかという問題をプログラマに押し付けることになりプログラマの負担が増えるのでfinal修飾されていなければならないという制約を設けたのかなと想像します。
まぁ、こっちの方が理にかなってますよね。