Java中類繼承、接口實現的一些要注意的細節問題


1.接口A和接口B有相同的方法,只是返回值不同,則實現類不能同時實現這兩個接口中的方法。

接口A有void C()方法,接口B有int C()方法,則無法同時實現這兩個接口。

Java為了彌補類單繼承的不足,引入了類多實現接口的機制,不過多實現某個接口也是有一定限制的,比如:

public interface A
{
    void C();
}
public interface B
{
    int C();
}

那么同時實現這兩個接口是不可能的:

這個錯誤是無法被修復的。試想,類AB實現接口A和接口B,那么接口A里面的抽象方法和接口B里面的抽象方法參數列表都相同僅有返回值不同,類AB應該實現哪個呢?實現接口A的"void C()",那么接口B的"int C()"怎么辦?實現接口B的"int C()"那么接口A的"void C()"怎么辦?因為"void C()"、"int C()"屬於方法參數相同,返回值不同,這兩個方法是不可以重載的,所以同時實現兩個方法也不可能。因此,在這里Java只能報錯了。

2.A是接口,B實現A,C繼承B,則C也是A的子類

有一個接口A,B實現了A接口,C繼承自B類,則C也是A的子類,看一下:

public interface A
{

}
public class B implements A
{

}
public class C extends B
{

}
public static void main(String[] args)
{
    C c = new C();
    System.out.println(c instanceof A);
}

返回結果是true。這是一個不難理解的結論,想到求證這個結論是因為有一次在研究LinkedHashMap的時候:

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>

既然LinkedHashMap已經繼承了HashMap了,HashMap是Map的實現類,那為什么LinkedHashMap還要實現Map呢,豈不是多此一舉嗎?由此想到了會不會是因為繼承了HashMap不代表LinkedHashMap是Map的子類所以LinkedHashMap要專門再實現一下Map,做了上面的實驗,發現是我多慮了,可能Sun的開發人員就想這么寫吧,呵呵。

 3.父子類有同名變量和同名方法的處理.

子類不會覆蓋父類的同名變量。

class Base {
    int count = 2;

    public void display() {
        System.out.println("Base : " + this.count);
    }
}

class Derived extends Base {
    int count = 20;

    @Override
    public void display() {
        System.out.println("Derived : " + this.count);
    }
}

public class Main {
    public static void main(String[] args) {
        Base bd = new Derived();
        System.out.println(bd.count);
        bd.display();
        Derived d = new Derived();
        Base d2b = d;
        System.out.println(d2b.count);
    }
}

 運行結果為:

2
Derived : 20
2

  

原因如下: 

編譯器有一條這樣的規定:編譯時類型由聲明該變量時使用的類型決定,運行時類型由實際賦給該變量的對象決定。


編譯器在處理方法和成員變量時存在區別。在子類沒有重寫父類的方法時,編譯器會將父類中的方法copy到子類中,如果子類復寫,則無法copy。對於實例變量而言卻不存在這樣的現象,子類定義的同名變量無法覆蓋父類的同名變量,就如同上面“2和20”的例子,兩個都存儲了,正是由於變量和方法之間的處理存在這樣的區別,所以對於一個引用類型的變量而言:

  • 訪問變量時,按照聲明該變量時的類型(編譯時類型)
  • 訪問方法時,按照實際引用的對象的類型(運行時類型)

因此,對於System.out.println(bd.count);而言,bd的編譯時類型為Base,因此就會輸出Base的count,也就是2;對於bd.display();而言,調用的是方法,bd的運行時類型為Derived,因此也就會調用了子類的display()方法。System.out.println(d2b.count);同理,會輸出編譯時類型Base的count了

4.注意父子類同名不同參數方法

上面的例子稍微修改一下:

class Father {
    public void foo(Object o) {
        System.out.println("Father.foo()");
    }
}

class Son extends Father{
    public void foo(String s) {
        System.out.println("Son.foo()");
    }
}

public class Main {
    public static void main(String[] args) {
        Son son = new Son();
        son.foo(new Object());
    }
}

  運行結果:

Father.foo()

  注意這個不是overload(方法重載),雖然方法名稱一樣,但是方法參數的類型不一樣,原則上也屬於兩個方法,曾經看到有人將上面也歸屬於方法重寫,個人表示有點異議。

      不同不同於上面的例子,son從父類中繼承到了foo(Object o)方法,son會根據傳入的參數不同而決定使用哪個方法。對於本例,傳入的是Object,因此son會使用繼承的foo方法,而非自己的。

 5.一個抽象類繼承接口,可以不實現接口的方法。

一個實現類繼承自一個抽象類並且實現了多個接口,那么必須實現所有未被實現的抽象方法

舉個例子:

public interface InterfaceA
{
    void A1();
    void A2();
}
public interface InterfaceB
{
    void B1();
    void B2();
}

public abstract class AbstractC implements InterfaceA, InterfaceB
{
    public void A1(){} // 我實現了InterfaceA的A1()方法
    public void B2(){} // 我實現了InterfaceB的B2()方法
    
    abstract void C(); // 我自己定義了一個抽象方法
}

那么要定義一個ClassD繼承自AbstractC,則必須:

 

public class ClassD extends AbstractC{
   public void A2(){} // 我必須實現InterfaceA中未被實現的A2()方法

   public void B1(){} // 我必須實現InterfaceB中未被實現的B1()方法

   void C(){} // 我必須實現AbstractC中未被實現的C()方法
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM