- PR -

あるクラスの paint メソッド内でサブクラスの paint メソッドを呼びたい

投稿者投稿内容
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2003-07-03 17:04
unibon です。こんにちわ。

#以下、例として AWT を挙げていますが、それはあくまでも例であり、
#疑問点としては Java の言語仕様(やオブジェクト指向的な考え)に関することです。

Java のメソッド(たとえば AWT の Canvas クラスの paint メソッド)内で
自身のクラスのスーパクラスのメソッドである super.paint を呼ぶことができますが、
逆にサブクラスの paint メソッドを呼ぶことはできるでしょうか。
sub.paint みたいな感じで。

たとえばですが AWT の Canvas クラスを継承して、
paint メソッドでの描画が5倍に拡大されるようなことを目論んだとします。
つぎのように作るととりあえず動きました。

--- MyCanvasTester.java ---
コード:
import java.awt.*;

abstract class MyCanvas extends Canvas { // abstract メソッドを持つので abstract な class にした。

    abstract public void paint2(Graphics graphics); // paint と別名のメソッドのインターフェースを定義。

    public void paint(Graphics canvasGraphics) { // 5 倍に拡大して表示する機能を持つ paint メソッド。
        super.paint(canvasGraphics);
        Image image = createImage(100, 100);
        Graphics imageGraphics = image.getGraphics();
        paint2(imageGraphics); // sub.paint(imageGraphics); 本当はサブクラスの同名メソッドを呼びたい。
        imageGraphics.dispose();
        canvasGraphics.drawImage(image, 0, 0, 500, 500, this);
    }
}

public class MyCanvasTester extends MyCanvas {

    public void paint2(Graphics graphics) { // 本当は paint メソッドにしたい。
        graphics.setColor(Color.blue);
        graphics.drawLine(0, 0, 100, 100);
    }

    public static void main(String[] args) {
        Frame frame = new Frame();
        Canvas canvas = new MyCanvasTester();
        frame.add(canvas);
        frame.setSize(new Dimension(600, 600));
        frame.setVisible(true);
    }
}


しかし、これだと新しく paint2 というメソッドを作らないといけないのですが、
本当はこのようなことを実現するのに、paint2 を使わずに、
つぎのように paint メソッドだけを使ったコードにしたいと思っています。
コード:
import java.awt.*;

class MyCanvas extends Canvas {

    public void paint(Graphics canvasGraphics) { // 5 倍に拡大して表示する機能を持つ paint メソッド。
        super.paint(canvasGraphics);
        Image image = createImage(100, 100);
        Graphics imageGraphics = image.getGraphics();
        sub.paint(imageGraphics); // super じゃなく sub を指定してサブクラスの paint メソッドを呼びたい。
        imageGraphics.dispose();
        canvasGraphics.drawImage(image, 0, 0, 500, 500, this);
    }
}

public class MyCanvasTester extends MyCanvas {

    public void paint(Graphics graphics) {
        graphics.setColor(Color.blue);
        graphics.drawLine(0, 0, 100, 100);
    }

    public static void main(String[] args) {
        Frame frame = new Frame();
        Canvas canvas = new MyCanvasTester();
        frame.add(canvas);
        frame.setSize(new Dimension(600, 600));
        frame.setVisible(true);
    }
}


このようなことはたぶん、無理なのだとは思いますが、
これはオブジェクト指向的にヘンな考えなのか、
それとも単に Java にそういう仕組みがないだけなのでしょうか。

疑問なのは、paint メソッドから別のメソッド(foo)は呼ぶことができるので、
サブクラスで foo メソッドをオーバライドすることで、
あるクラスの paint メソッドから、そのサブクラスの foo メソッドを呼ぶことができます。
これと同じようなことが同名のメソッドだとできない、
すなわち、あるクラスの paint メソッドからサブクラスの paint を呼べないのは、
なぜなのでしょうか。
かずくん
ぬし
会議室デビュー日: 2003/01/08
投稿数: 759
お住まい・勤務地: 太陽系第三惑星
投稿日時: 2003-07-03 18:13
田村と申します

abstract class MyCanvas extends Canvas {
 // template method
 protected abstract void paintsub(Graphics canvasGraphics);

....

 public void paint(Graphics canvasGraphics) {
  // 共通部分を記述
  ....

  paintsub(canvasGraphics); // sub-classで実装

  // 共通部分を記述
  ...
 }
}

上述したコードのように、Templete Method パターンで記述する方法ではだめでしょうか?メソッドの名前は変わってしまいますが、同様の事を行えることができると思います。
maru
ぬし
会議室デビュー日: 2003/01/27
投稿数: 412
投稿日時: 2003-07-03 21:26
こんにちは。

このスレッドを読んでまず思ったこと。

1.サブクラスでスーパークラスと同一名メソッドを書いた時点でオーバーライトになり
 同一名のメソッド呼び出しはサブクラスのものから呼び出されるので、スーパークラス
 の同一名メソッドを先に呼び出すのは無理では?

2.仮にスーパークラスが、サブクラスの同一名メソッドを呼び出せる仕組みになっていた
 場合、それを知らずにサブクラスからスーパークラスの同一名メソッドを呼び出す
 ソースを書いた場合、お互い呼び出しあってアウトオブメモリーで落ちるので危険?

ではないでしょうか?


maru
ぬし
会議室デビュー日: 2003/01/27
投稿数: 412
投稿日時: 2003-07-03 21:36
以下のようにインターフェースを使って無理やり、スーパークラスからサブクラスの同一
名メソッドを呼び出すようにしたのですが、そもそもサブクラスの方を先によぶので
スーパークラス側のメソッドは呼び出されませんでした。あたりまえか・・・。
ちなみに、サブクラスでスーパークラスの同一名メソッドをインターフェースのメソッド
として実装しても、スーパークラスの同一名メソッドであればスーパークラスのオーバー
ライトとして扱われるようですね。

import java.awt.*;

class MyCanvas extends Canvas {

private MyCanvasEvents evnts;
public MyCanvas(){
super();
}
public void setEvents( MyCanvasEvents evnts ){
this.evnts = evnts;
}
public void paint(Graphics canvasGraphics) {
super.paint(canvasGraphics);
Image image = createImage(100, 100);
Graphics imageGraphics = image.getGraphics();
evnts.paint(imageGraphics);
imageGraphics.dispose();
canvasGraphics.drawImage(image, 0, 0, 500, 500, this);
System.out.println("SUPER");
}
}

public class MyCanvasTester extends MyCanvas implements MyCanvasEvents{
public MyCanvasTester(){
super();
setEvents(this);
}
public void paint(Graphics graphics) {
super.paint(graphics);
graphics.setColor(Color.blue);
graphics.drawLine(0, 0, 100, 100);
System.out.println("SUB");
}

public static void main(String[] args) {
Frame frame = new Frame();
Canvas canvas = new MyCanvasTester();
frame.add(canvas);
frame.setSize(new Dimension(600, 600));
frame.setVisible(true);
}
}

interface MyCanvasEvents{
public void paint(Graphics graphics);
}
Astmild
常連さん
会議室デビュー日: 2003/06/09
投稿数: 30
お住まい・勤務地: 大田区
投稿日時: 2003-07-03 22:08
unibon さん、こんにちは。

引用:
疑問なのは、paint メソッドから別のメソッド(foo)は呼ぶことができるので、
サブクラスで foo メソッドをオーバライドすることで、
あるクラスの paint メソッドから、そのサブクラスの foo メソッドを呼ぶことができます。


スーパークラスからサブクラスのメソッドを呼ぶことは、同名じゃなくても出来ないんじゃ
ないでしょうか。下記のコードで試してみました。(ファイル名:PrintTest.java)

コード:
public class PrintTest{
    public static void main(String[] args){
        Print print1 = new PrintSub1();
        print1.printer();

        Print print2 = new PrintSub2();
        print2.printer();
    }
}


abstract class Print{
    public void printer(){
        System.out.println( this.getClass().getName() + "クラスで実行中");
        printing();
        printing2();
    }
    public void printing(){
        System.out.println("a");
    }
    abstract public void printing2();
}

class PrintSub1 extends Print{
    public void printing(){
        super.printing();
        System.out.println("b");
    }
    public void printing2(){
        System.out.println("!");
    }
}

class PrintSub2 extends Print{
    public void printing(){
        super.printing();
        System.out.println("c");
    }
    public void printing2(){
        System.out.println("?");
    }
}


ソースコード上、スーパークラスのprinter()がサブクラスのprinting2()を呼んでいるように
見えますが、結果は同じサブクラス内で処理されてました。
英-Ran
ベテラン
会議室デビュー日: 2002/06/12
投稿数: 55
投稿日時: 2003-07-03 23:31
引用:
あるクラスの paint メソッドからサブクラスの paint を呼べないのは、
なぜなのでしょうか。



たぶん、継承の概念の基本として「子は親を知っているが、親は子を知らない」ということがあるからだと思います。Javaは型の縛りが強い言語なので、定義されていないものを参照しようとしてもコンパイルが通らない。そして、存在するかどうかもわからないサブクラスのメソッドは呼び出せない(paintメソッドをサブクラスが実装してくれるという保証は"普通は"ないわけですから)

……とはいえ、下記のようなコードだったら、サブクラスでの実装が保証されるわけだから問題ない気もします。どなたかこのようなコードが規制されなければならない理由とか知っていたら教えてください(^_^;

コード:
public abstract class SuperCanvas {

public abstract void paint(Graphics g) {
g.setColor(Color.RED);
sub.paint(g);
}
}

public class SubCanvas extends SuperCanvas {
public void paint(Graphics g) {
...
}
}



ひとつ理由として思いつくのは、maruさんが言っているように、サブクラスのpaintメソッドでsuper.paintを呼ぶと無限ループに陥ること。でも、それは言語仕様で禁止するという方法もあるわけで……

[ メッセージ編集済み 編集者: 英-Ran 編集日時 2003-07-04 00:05 ]
Kensaku
常連さん
会議室デビュー日: 2003/02/16
投稿数: 22
投稿日時: 2003-07-03 23:39
sub.paint()ができるようにしたら、ポリモーフィズム(多態性)ができなくなってしまうと思います。
英-Ran
ベテラン
会議室デビュー日: 2002/06/12
投稿数: 55
投稿日時: 2003-07-03 23:51
引用:
ポリモーフィズム(多態性)ができなくなってしまうと思います。



すみません、abstractと書いてしまったために誤解を招いてしまったような気がします。意図としては、「サブクラスのメソッドよりスーパークラスのメソッドを先に呼ぶ」という逆オーバーライド的な機能を実現すると言うつもりでした。

# そのためにはabstractではない別のキーワードが必要ですね……

[ メッセージ編集済み 編集者: 英-Ran 編集日時 2003-07-03 23:53 ]

スキルアップ/キャリアアップ(JOB@IT)