大変お世話になっております。
反逆する武士
uematu tubasaです。
初回投稿日時:2023年6月24日(令和5年6月24日)
インターフェースの基礎知識を問う問題
インターフェースはクラスから「型」だけを取り出したものです。
言い換えるならば、インターフェースは他のクラスからの「扱い方」を規定したものです。
他のクラスから扱えるようにするために、規定する抽象メソッドはすべてpublicであると解釈されます。
仮にアクセス修飾子を記述しなくても、インターフェースは次のようにコンパイラによってpublicで修飾されます。
public interface Sample{
void hello(); ←コンパイラによってpublicで修飾される。
}
※↓↓↓注意事項↓↓↓※
インターフェースに定義する抽象メソッドは、protectedやprivateで修飾することはできません。
Javaというプログラミング言語では、クラスの多重継承は禁止されていますが、インターフェースの多重実現は認められています。
多重実現の場合はカンマ区切りで列挙することになります。
また、インターフェースには、それを実現したクラスが持つべき抽象メソッドを宣言します。
その抽象メソッドの中身(実装)を持つことはできません。
インターフェースには、2つのルールを満たすフィールドであれば、記述することが可能です。
1、finalを使って、動的に値が変更されないこと(つまり定数)
2、staticを使って、インスタンスが生成できなくても使えること
public class ConcreateClass implements InterfaceA, InterfaceB {) ←多重実現
public interface Sample{
public void hello(){}←コンパイルエラー※抽象メソッドの中身(実装)を持つことはできないから。
}
そして、インターフェースは継承することが可能で、抽象クラスにおいてはインターフェースのメソッドを実装する必要はありません。
インターフェースのデフォルトメソッド問題
public interface A {
default void sample(){
System.out.println("sample");
}
}
仮に、インターフェースでdefault修飾子をメソッドに付ければ、複数のクラスに実現する場合でも、共通処理をそれぞれオーバーライドしなくても良くなります。
なお、デフォルトメソッドもインターフェースに定義するメソッドと同様にpublicで修飾されます。
デフォルトメソッドをオーバーライドしたメソッドから元のデフォルトメソッドを呼び出すには、次の構文を使います。
インターフェース名.super.メソッド名();
ただし、継承を繰り返したサブクラスのサブクラスでは通用しない。
抽象クラスの基礎知識
抽象クラスは、インターフェースとクラスの両方の性質を持ったクラスです。
つまり、抽象クラスは実装を持つ具象メソッドと、実装を持たない抽象メソッドの両方を持つことができます。
public abstract class AbstractSample{
// 具象メソッド(サブクラスが引き継ぐ)
public void methodA(){
// any code
}
// 抽象メソッド(サブクラスで実装する)
public abstract void methodB();
}
抽象クラスはインスタンス化はできません。
抽象クラスの継承は具象クラスだけでなく、抽象クラスでも可能です。
サブクラスから抽象クラスの公開フィールドに自由にアクセスできる。
抽象クラスの呼び出し問題
以下のソースコードをコンパイル、実行した場合の結果はどうなるのか。
abstract class AbstractSample {
public void sample() {
System.out.println("A");
test();
System.out.println("C");
}
protected abstract void test();
}
public class ConcreateSample extends AbstractSample {
@Override
protected void test() {
System.out.println("B");
}
}
public class Main {
public static void main(String[] args) {
AbstractSample s = new ConcreateSample();
s.sample();
}
}
実行結果:A、B、Cとコンソール出力される。
【解説】
間違いやすいポイントとしてはConcreateSampleクラスをインスタンス化したが、それを抽象クラスのインスタンスとして扱うことにある。
抽象クラスを継承しているConcreateSampleクラスのtestメソッドはprotectedだが、同じパッケージに属しているという前提から、Mainクラスから参照しても問題なし。
抽象クラスの具象メソッドを実行すると、抽象クラスの具象メソッドが実行され、Aとコンソール出力された後、ConcreateSampleクラスのtestメソッドに処理が移る。
その結果、Bとコンソール出力される。
その後処理は抽象クラスの具象メソッドに戻り、Cとコンソール出力される。
※ちなみに、MainクラスでConcreateSampleクラスをインスタンス化する際、ConcreateSample型として扱ったとしても処理内容は変わらず。
オーバーライドに関する基礎知識問題
何らかのクラスを継承したクラスにて、メソッドを再定義することをオーバーライドと言います。
要するに、メソッドの上書きです。
オーバーライドを実施するため、メソッドのシグニチャ(メソッド名、引数リストの型、数、順番)は同じでなければなりません。
戻り値型は異なっても問題ありません。
public int Aをオーバーライドして、public String Aにすることは問題なし。
※共変戻り値という機能がJavaSE5から導入された模様。
言い換えるならば、メソッドの処理内容やアクセス修飾子は書き直していいのですが、それ以外は駄目って話です。
アクセス修飾子はオーバーライドにする際、広くするのは問題なしだが、狭くすることはできない。
継承関係にある2つのクラスで同名のフィールドが使用された場合の挙動問題
class A {
String val = "A";
void print(){
System.out.print(val);
}
}
class B extends A {
String val = "B";
}
public static void main(String[] args) {
A a = new A();
A b = new B();
System.out.print(a.val);
System.out.print(b.val);
a.print();
b.print();
}
【問題】上記のソースコードをコンパイル、実行した場合の結果としてどうなるか。
【回答】「AAAA」と表示される。
【解説】サブクラスで、スーパークラスに定義されているフィールドと同じ名前のフィールドを再定義することは可能です。
したがって、サブクラスを定義する際にコンパイルエラーになることはありません。
このような場合、どちらのフィールドが優先されるのかを理解しなければなりません。
1、フィールドを参照した場合には、変数の型で宣言された方を使う。
2、メソッドを呼び出した場合には、メソッド内の指示に従う。
Bクラスのインスタンスであるbにおいて、printメソッドを使用することはできるが、printメソッドはAクラスのフィールドを使用するメソッドであるため、Aが出力される。
※試験対策※
Aを継承したBであっても、printメソッドを実行する場合は、Aに処理が移るから、ある意味当然。
インターフェースの実装とクラスの継承の違いを突いてくる問題
public interface A {}
public class B implements A {}
public class C extends B {}
public class D {}
public static void main(String[] args){
A[] array = {
new B(),
new C(),
new A(),
new D()
};
}
【問題】上記のソースコードをコンパイル、実行した場合の結果は何か。
【回答】9行目と10行目でコンパイルエラーになる。
【解説】A型の配列を格納する必要がある。
したがって、BやCはAを実装、それを継承しているからコンパイルエラーにはならない。
Aはインターフェースなので、インスタンス化できない。
継承関係にあるクラスのコンストラクタの動作問題
継承関係にあるクラスのインスタンスはスーパークラスと差分のインスタンスで構成されています。
したがって、スーパークラスがインスタンス化していないとサブクラスはインスタンスができません。
インスタンス化されたときに実行されるのがコンストラクタなので、サブクラスとスーパークラスのコンストラクタが同時に実行される。
それを踏まえて以下の問題。
class A {
public A(){
System.out.println("A");
}
}
class B extends A {
public B(){
System.out.println("B");
}
}
public static void main(String[] args){
A a = new B();
}
【問題】上記のソースコードをコンパイル、実行した場合にどうなるのか。
【回答】「A」「B」とコンソール出力される。
【解説】Bがインスタンス化されるときに、AとBのコンストラクタが実行される。
順番としては、スーパークラスのコンストラクタ⇒サブクラスのコンストラクタなので、Aの次にBがコンソール出力される。
以上です。