アプリ版:「スタンプのみでお礼する」機能のリリースについて

「インタフェースを実装してそれが持つ抽象メソッドをオーバーライドする」は正しい?


はじめまして。Javaのインタフェースに関する質問です。

私はこれまで、インタフェースを使うときは、インタフェースを実装してクラスを宣言し、そのクラス、またはサブクラスでインタフェースがもつすべての抽象メソッドを定義する、と理解していました。

しかし、下の例をみてください。抽象メソッドの定義を、インタフェースの実装の以前で与えています。問題無くコンパイルでき、実行できます。実行結果も以下の通りです。

インタフェースの抽象メソッドへの定義の与え方やその実行のされ方は、メソッドのオーバーライドと同様と思っていましたので、下記のコードでは「クラスBが抽象クラスではありません」や、「インタフェースの抽象メソッドがオーバーライドされていません」などの文法エラーがでると思っていました。

そこで、質問です。
インタフェースが持つ抽象メソッドの定義を与える場所について、または、これに関する説明のあるページなど、何かご存知でしたら教えてください。

★コード★
interface X{
  void show();
}

class A{
  public void show(){
    System.out.println("A");
  }
}

class B extends A implements X{
}

public class Main{
  public static void main(String[] args){
    X x=new B();
    x.show();
  }
}

★実行結果★
>java Main
A

★Java環境★
java 1.6.0_21
javac 1.6.0_16

A 回答 (6件)

> インタフェース型の変数を用いた場合の、メソッドの宣言の見つけ方は、メソッドのオーバーライドの場合とは別物なのでしょうか?



JavaプログラムをJavaバイトコードにコンパイルすると普通のインスタンスメソッドの呼び出しはinvokevirtualに、インタフェース経由のメソッド呼び出しはinvokeinterfaceになります。しかし、これらのバイトコードが実行時にメソッドを探し出す手順は同じです。

interface I { void m(); }
class B extends A { ~ }
class C extends B implements I { ~ }
class D extends C { ~ }
というクラス階層があったとして、
I obj = new D();
obj.m();
というメソッド呼び出しがあった場合、
・Dにm()の定義があればそれを呼ぶ
・DになければC→B→Aとクラス階層を順にさかのぼってm()の定義を探す
となります。
インタフェースだからと言って特別な事はしていないし、Iで宣言されているメソッドの実体がAにあっても問題ないことが分かると思います。
    • good
    • 0
この回答へのお礼

salsberryさん

的確なご回答有難うございました。
頂いたキーワードinvokevirtualとinvokevirtualからVMの仕様書に辿りつきました。
The JavaTM Virtual Machine Specification
http://java.sun.com/docs/books/jvms/second_editi …

仕様書内では、インタフェース型の変数を用いた場合のメソッドの検索手順とクラス型の変数を用いた場合のメソッドの検索手順が明確に記述されていました。細かな違いはあるものの、ほぼ同じ手順で検索されていること、またその手順そのものが分かりました。

salsberryさんのご指摘通りで、メソッドの探索は、末端のサブクラスからスーパークラスの方へ向って行われるんですね。

結果的に、インタフェースの抽象メソッドの実体は、インタフェースが実装されているクラスの前後どこかにあれば良いとなりますね。

有難うございました。

お礼日時:2010/08/10 03:21

こんにちは。



補足、拝見しましたが・・・、
ちょっと質問の意味がわかりかねます。
どのようなところが疑問なのでしょう?
例えば・・・、で結構ですので、補足して頂けますか?
    • good
    • 0
この回答へのお礼

taka451213さん

質問が分かり難くて申し訳ありませんでした。明確にすると、次のコードを実行したとき、(1)の場合と(2)の場合では、メソッドの探索の仕方は異なりますか?というものでした。

その後、皆さんからのご回答を参考にあちこち情報を探し回りました。その結果、(1)と(2)ではメソッドの探索手順をほぼ同じことが分かり、末端のサブクラスからスーパークラスへと再帰的に探すということでした。

結果的に、皆さんからご指摘頂いたように「インタフェースがもつ抽象メソッドの定義は、インタフェースを実装しているクラス、またはそのスーパークラスかサブクラスのどこかにあれば良い」となりますね。

ご回答有難うございました。

★コード★
interface X{
  void show1();
}

class A{
  public void show1(){
    System.out.println("A1");
  }
  public void show2(){
    System.out.println("A2");
  }
}

class B extends A implements X{
  public void show2(){
    System.out.println("B2");
  }
}

public class Main{
  public static void main(String[] args){
    B b=new B();
    A a=b;
    X x=b;
    
    x.show1(); // (1)インタフェース型変数
    a.show2(); // (2)クラス型変数
  }
}

★実行結果★
>java Main
A1
B2

お礼日時:2010/08/10 03:22

確認したいことがあります.


「もし、メソッドのオーバーライドの要領で、スーパークラス(インタフェース)に宣言されているメソッドを実行しようとしたら、サブクラス(インタフェースが実装されたクラス、またはこのクラスのサブクラス)で宣言されている同じメソッドが実行されるとは、考えることができなくなりそうです。」
というのは, どういうことでしょうか? 基本的には同じようにメソッドを検索するような気がするのですが....
    • good
    • 0
この回答へのお礼

Tacosanさん

Tacosanのおっしゃる通り、インタフェース型の変数を用いた場合とクラス型の変数を用いた場合において、メソッドを検索する手順はほぼ同じであることが分かりました。

「もし、メソッドのオーバーライドの要領~」は、メソッドの探索手順についての私の誤解でした。例えば、以下のコードを見て下さい。(1)と(2)の個所で両者ともにクラスAのshow()メソッドを呼び出そうとしています。結果的にはオーバーライドの機能により実行されるメソッドはサブクラスCに宣言されているものです。クラス型の変数を用いてメソッドにアクセスする場合は、常に最も下で宣言されているメソッドが実行されます。この点から生じた誤解でした。

★コード★
class A{
  public void show(){
    System.out.println("A");
  }
}

class B extends A{
}

class C extends B{
  public void show(){
    System.out.println("B");
  }
}

public class Main{
  public static void main(String[] args){
    C c=new C();
    B b=c;
    A a=c;

    a.show(); //(1)
    b.show(); //(2)
}
}

★実行結果★
>java Main
B
B

メソッドを探索する手順は、正しくは、「スーパークラス(インタフェース)に宣言されているメソッドを実行しようとしたら、末端のサブクラス(インタフェースが実装されたクラスの末端のサブクラス)からスーパークラスへ再帰的に同じメソッドを探索し、見つかったメソッドを実行する」となりそうです。

ご回答有難うございました。

お礼日時:2010/08/10 03:24

> 私はこれまで、インタフェースを使うときは、インタフェースを実装してクラスを宣言し、


> そのクラス、またはサブクラスでインタフェースがもつすべての抽象メソッドを定義する、
> と理解していました。

> しかし、下の例をみてください。抽象メソッドの定義を、インタフェースの実装の以前で
> 与えています。問題無くコンパイルでき、実行できます。実行結果も以下の通りです。

抽象メソッドの定義(インタフェースの実装)を前に書くか後に書くかは、関係ありません。

インタフェースXをimplementsしたクラスBにshowメソッドが実装されていればよいわけです。

クラスBの定義部分には、showメソッドは書いてありあせんが、クラスAを継承しており、クラスAでshowメソッドが実装されている為、クラスBで、showが実装されていると言えます。
例えば、次の例で、クラスBの定義部分には、showメソッドがありませんが、クラスBをnewした変数bで、showメソッドが使えます。
これは、クラスAを継承している為、クラスBで、showが実装されている事になるからです。

class A{
  public void show(){
    System.out.println("A");
  }
}

class B extends A {
}

public class Main{
  public static void main(String[] args){
    B b=new B();
    b.show();
  }
}

http://msugai.fc2web.com/java/interface.html
上記ページにインタフェースの説明がありますが、「インタフェースの実装」の部分で、次のような記述があります。
> インタフェースを実装したら、そこで定義されている抽象メソッドを全て実装しておかないと
> コンパイルエラーになります。
ここで、抽象メソッドを全て実装するのは、そのクラスの定義部分でも継承元でもかまいません。
ただし、publicで実装しなければなりません。
継承元にpublicで実装してあれば、継承先でもそのメソッドを実装している事になります。
    • good
    • 0
この回答へのお礼

yossy_sas2000さん


ご回答有難うございました。

>抽象メソッドの定義(インタフェースの実装)を前に書くか後に書くかは、関係ありません。

この点に関するホームページを紹介して頂き有難うございます。確認することができました。

お礼日時:2010/08/10 03:14

#1 と同じなんだけど, 結局


・インターフェイスX を実装する具象クラスは void show() を持っていなければならない
・クラスB は (クラスA から継承した) void show() を持っている
ので大丈夫, と.

この回答への補足

早々のご回答有難うございます。

Tacosanさんのお考えも、taka451213さんと同じようですね。
taka451213さんへ再度質問させて頂きましたが、インタフェース型の変数で、そのインタフェースがもつ抽象メソッドへアクセスした場合に、どのように実際のメソッドの定義を見つけるのかについて何かご存知でしたらお知らせお願いします。

質問内容は、taka451213さんへの補足をご参照頂ければと思います。

補足日時:2010/08/09 11:04
    • good
    • 0

こんばんは。



特に不思議ではないですが・・・?
クラスBはクラスAをextendsしているので、interface Xをimplementsするための要件は満たしています。
ただ、このimplementsをBに書くのに違和感を感じますが・・・。
まぁ、言語仕様的にはOKという事で・・・。

この回答への補足

早々のご回答有難うございます。

taka451213さんのお考えでは、
インタフェースがもつ抽象メソッドの定義は、インタフェースを実装しているクラス、またはそのスーパークラスかサブクラスのどこかにあれば良いということですね。

このとき、1つだけ引っかかったのがインタフェース型の変数というものです。このインタフェースを実装したクラス、またはこのクラスのサブクラスのオブジェクトは、このインタフェース型の変数で扱うことが多いと思いますが、その場合、どのように抽象メソッドの定義を見つけるのかをどのように理解したらよいのか分からなくなります。

もし、メソッドのオーバーライドの要領で、スーパークラス(インタフェース)に宣言されているメソッドを実行しようとしたら、サブクラス(インタフェースが実装されたクラス、またはこのクラスのサブクラス)で宣言されている同じメソッドが実行されるとは、考えることができなくなりそうです。

そこで、もつ1つ質問させてください。
インタフェース型の変数を用いた場合の、メソッドの宣言の見つけ方は、メソッドのオーバーライドの場合とは別物なのでしょうか?

Javaの仕様書に目を通してみていますが、これに関する記述が見当たらなくて・・・。
細かい質問で申し訳ありませんが、何か情報ありましたらお知らせください。

補足日時:2010/08/09 10:57
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!