みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

staticメソッドの継承

お世話になります。
static メソッドの継承についてなのですが、

class Parent {
 public static String name = "Parent";
 public static getName() {
  return name;
 }
}

class Child extends Parent {
 public static String name = "Child";
}

だと、

Parent.getName();
Child.getName();

はともに Parent を返します。
ChildにてgetName()をオーバーライドすれば望む結果が得られるのですが、何かスマートでは無いような気がしてしまいます。

継承したstaticメソッドは親の元で実行され、そしてアクセスするstatic変数が親というのは何故なのでしょうか?
根本的な質問かもしれませんが、よろしくお願いいたします。

投稿日時 - 2006-07-15 14:00:05

QNo.2277327

暇なときに回答ください

質問者が選んだベストアンサー

static なフィールドやメソッドというのは
(正確な説明ではないかもしれませんが)
他の言語で言うところの
「グローバル変数」「グローバル関数」みたいなものです。

つまり、

class Parent {
 public static String name = "Parent";
 public static String getName() {
  return name;
 }
}
class Child extends Parent {
 public static String name = "Child";
}

上のコードは、
下のようなものだと考えてみてください。

String Parent_name = "Parent";
String Child_name = "Child";
String Parent_getName() {
 return Parent_name;
}

Child.getName() が
"Child" でなく "Parent" を返す理由が分かると思います。

単純にクラス名を取得したいのであれば、
 Parent.class.getName()
 Child.class.getName()
を使うという方法があります。

もうすこし一般的な解決法としては、
ハッシュテーブルを使ってみるのもよいかもしれません。

Hashtable table = new Hashtable();
table.put(Parent.class, "Parent");
table.put(Child.class, "Child");
String p = (String)table.get(Parent.class);
String c = (String)table.get(Child.class);

Java 5.0 では下のような書き方になります。

Map<Class,String> table = new HashMap<Class,String>();
table.put(Parent.class, "Parent");
table.put(Child.class, "Child");
String p = table.get(Parent.class);
String c = table.get(Child.class);

投稿日時 - 2006-07-18 02:08:34

ANo.4

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(5)

ANo.5

staticなフィールドはクラスに張り付いています。つまり、オブジェクトを2つ作った場合にstaticでないフィールドはそれぞれのオブジェクトについてフィールドが生成されますが、staticなものはクラスに張り付いているので、そのフィールドはクラス間で共有されています。

そして、サブクラスでもその関係は同等です。あなたが書いている通りサブクラスでフィールドの値を置き換えた場合、staticでないものに関してはそれぞれのオブジェクトに関してフィールドが生成されているのでスーパークラスとサブクラスで値を変えることができますが、staticなものに関してはフィールドを共有しているので、サブクラスで値を変えられたときはスーパークラスまでその変更が影響してきます。

スーパークラスとサブクラスで共有しているフィールドを返そうとしているのに、違う値を返すことはできるでしょうか?当然できませんね。だから、オーバーライドするのが正解でしょう。

>いちいちサブクラスでオーバライドするのが面倒くさい
とありますが、サブクラスのコンストラクタでフィールドを変更するコードをかく手間とどこが違うのかが自分にはよくわかりません。
見た目が美しいか美しくないかと言う感覚ならよくわかりますが。


同じオブジェクト、違うオブジェクトに関しては下記のコードを試してみてください。よくわかると思います。

public class Parent {
protected static StringBuffer p_sb1=new StringBuffer("parent_sb1");
protected StringBuffer p_sb2=new StringBuffer("parent_sb2");

static StringBuffer get_sb1()
{
return p_sb1;
}

StringBuffer get_sb2()
{
return p_sb2;
}

public static void main(String args[])
{
Parent p1=new Parent();
Parent p2=new Parent();

System.out.println("p1.p_sb1とp2.p_sb1は"+checkSameObj(p1.p_sb1,p2.p_sb1));//staticなフィールドにはstaticにアクセスするべき
System.out.println("p1.p_sb2とp2.p_sb2は"+checkSameObj(p1.p_sb2,p2.p_sb2));
System.out.println("p1.get_sb1()とp2.get_sb1()は"+checkSameObj(p1.get_sb1(),p2.get_sb1()));//staticなフィールドにはstaticにアクセスするべき
System.out.println("p1.get_sb2()とp2.get_sb2()は"+checkSameObj(p1.get_sb2(),p2.get_sb2()));
System.out.println("----------");
System.out.println("Parent.p_sb1とChild.p_sb1は"+checkSameObj(Parent.p_sb1,Child.p_sb1));
System.out.println("Parent.get_sb1()とChild.get_sb1()は"+checkSameObj(Parent.get_sb1(),Child.get_sb1()));
System.out.println("----------");

Child c1=new Child();

System.out.println("p1.p_sb1とc1.p_sb1は"+checkSameObj(p1.p_sb1,c1.p_sb1));//staticなフィールドにはstaticにアクセスするべき
System.out.println("p1.p_sb2とc1.p_sb2は"+checkSameObj(p1.p_sb2,c1.p_sb2));
System.out.println("p1.get_sb1()とc1.get_sb1()は"+checkSameObj(p1.get_sb1(),c1.get_sb1()));//staticなフィールドにはstaticにアクセスするべき
System.out.println("p1.get_sb2()とc1.get_sb2()は"+checkSameObj(p1.get_sb2(),c1.get_sb2()));
System.out.println("----------");
System.out.println("p1.p_sb1とp2.p_sb1は"+checkSameObj(p1.p_sb1,p2.p_sb1));//staticなフィールドにはstaticにアクセスするべき
System.out.println("p1.p_sb2とp2.p_sb2は"+checkSameObj(p1.p_sb2,p2.p_sb2));
System.out.println("p1.get_sb1()とp2.get_sb1()は"+checkSameObj(p1.get_sb1(),p2.get_sb1()));//staticなフィールドにはstaticにアクセスするべき
System.out.println("p1.get_sb2()とp2.get_sb2()は"+checkSameObj(p1.get_sb2(),p2.get_sb2()));
System.out.println("----------");
System.out.println("Parent.p_sb1とChild.p_sb1は"+checkSameObj(Parent.p_sb1,Child.p_sb1));
System.out.println("Parent.get_sb1()とChild.get_sb1()は"+checkSameObj(Parent.get_sb1(),Child.get_sb1()));
}

static String checkSameObj(Object o1,Object o2)
{
if(o1==o2)
{
return "同じオブジェクト/o1:"+o1+",o2:"+o2;
}
else
{
return "違うオブジェクト/o1:"+o1+",o2:"+o2;
}
}
}

class Child extends Parent
{
static
{
p_sb1=new StringBuffer("child_sb1");
}

Child()
{
p_sb2=new StringBuffer("child_sb2");
}
}

投稿日時 - 2006-07-19 00:56:03

ANo.3

UKY

まずは static ではないメソッドで似たようなことをやるとどうなるか試してみるとよろしいかと。


> class Child extends Parent {
> static{ name = "Child"; }
> }

それをやると、Child が初期化される前は getName() は "Parent" を返し Child が初期化された後は getName() は "Child" を返すというとんでもない混乱が待ち受けています。

投稿日時 - 2006-07-16 12:52:53

お礼

> まずは static ではないメソッドで似たようなことをやるとどうなるか試してみるとよろしいかと。

ちょっと質問の例が悪かったのですが、オブジェクトの場合、

class Parent {
 private String name = "Parent";
 public getName() {
  return name;
 }

}
class Child extends Parent {
 public Child() {
  name = "Child";
 }
}

と書くことで、メソッドのオーバーライドをする必要がありませんが、static でも同じようなことができないかなと思って質問を致しました(説明が不十分でもうしわけありません)


何かよいデザインパターンがあればよいのですが。
(いちいちサブクラスでオーバライドするのが面倒くさい)

ありがとうございます。

投稿日時 - 2006-07-16 21:29:49

ANo.2

親にある関数から参照出来るのは、親が持っている変数のみです。
子側では、新たに変数宣言するのではなく、親から受け継いだ変数の中身を変更すればいいのだと思いますけど?
static メンバの変更なので静的初期化子内で

class Child extends Parent {
static{ name = "Child"; }
}

投稿日時 - 2006-07-15 21:11:27

お礼

> class Child extends Parent {
> static{ name = "Child"; }
> }

static{}
という書き方があること、勉強になりました。
ただ、なぜかおっしゃるとおりにしたのですが、どうも "Child" で初期化されませんでした。
初期化されるタイミングがあるのでしょうか。

また、静的初期化子 + メソッドをオーバーライドして試してみたのですが、#3さんがおっしゃっていた通り、親(Parent)の値も丸々書き換えてしまうようです。

ありがとうございました。

投稿日時 - 2006-07-16 21:22:22

ANo.1

Parent.getNameは定義しているけど、Child.getNameは定義してないんですよね。
それなら単純に、Child.getNameは存在しないので親クラスのParent.getNameが実行され、Parent.getNameのスコープにはChild.nameは存在しないのでParent.nameが参照されているだけです。
staticでなくても同様にChild.getNameはParentを返します。

投稿日時 - 2006-07-15 19:42:12

お礼

> staticでなくても同様にChild.getNameはParentを返します。

なるほど、たしかにおっしゃるとおりです。
やはりサブクラスでもオーバーライドしなければいけないみたいですね・・・。

ありがとうございます。

投稿日時 - 2006-07-16 21:17:01