JAVA只要掌握內部類,多繼承和單繼承都不是問題
摘要:如果實現java的多繼承,其實很簡單,關鍵是對於內部類的特徵的掌握,內部類可以繼承一個與外部類無關的類,保證了內部類天然獨立性,根據這個特性從而實現一個類可以繼承多個類的效果。
本文分享自華為雲社群《【JAVA冷知識】JAVA居然支援多繼承?讓我們用內部類去吧!》,作者:山河已無恙 。
眾多周知,對於面嚮物件語言來講,JAVA是不支援多繼承的,只支援單繼承,但是提供了介面來補償。
在實際的專案中,介面更多的用於行為的委託,把類本身一些是共性但又是特定的行為委託給一個介面的具體實現,當然介面也可以用於屬性的委託,物件結構型的設計模式大都採用介面的方式來實現對物件內部組成的註冊和操作。
如果實現java的多繼承,其實很簡單,關鍵是對於內部類的特徵的掌握,內部類可以繼承一個與外部類無關的類,保證了內部類天然獨立性,根據這個特性從而實現一個類可以繼承多個類的效果。
下面我們看一個Demo,宣告父母兩個介面,實現父母兩個類,看如何通過內部類來繼承父母類,而不是通過,介面委託的方式。
一個Demo
父親介面
package com.liruilong; /** * @Project_name: workspack * @Package: com.liruilong * @Description: 父親介面 * @Author: [email protected] * @WeChat_Official_Accounts: 山河已無恙 * @blog: https://liruilong.blog.csdn.net/ * @Date: 2022/2/12 2:48*/ public interface Father { /** * @return: int * @Description 強壯的行為 * @author LiRuilong * @date 2022/2/12 2:49 **/ int strong(); }
父親實現類
package com.liruilong; /** * @Project_name: workspack * @Package: com.liruilong * @Description: 父親類 * @Author: [email protected] * @WeChat_Official_Accounts: 山河已無恙 * @blog:https://liruilong.blog.csdn.net/ * @Date: 2022/2/12 2:51 */ public class FatherImpl implements Father { static public String height = "身體超高"; /** * @return: int * @Description 強壯值 * @author LiRuilong * @date 2022/2/12 2:51 **/ @Override public int strong() { return 8; } }
母親介面
package com.liruilong; /** * @Project_name: workspack * @Package: com.liruilong * @Description: 母親介面 * @Author: [email protected] * @WeChat_Official_Accounts: 山河已無恙 * @blog: https://liruilong.blog.csdn.net/ * @Date: 2022/2/12 2:50 */ public interface Mother { /** * @return: int * @Description 溫柔的行為 * @author LiRuilong * @date 2022/2/12 2:50 **/ int Kind(); }
母親實現類
package com.liruilong; /** * @Project_name: workspack * @Package: com.liruilong * @Description: 母親類 * @Author: [email protected] * @WeChat_Official_Accounts: 山河已無恙 * @blog: https://liruilong.blog.csdn.net/ * @Date: 2022/2/12 2:51 */ public class MotherImpl implements Mother{ static public String pretty = "臉蛋特別漂亮"; /** * @return: int * @Description 溫柔值 * @author LiRuilong * @date 2022/2/12 2:51 **/ @Override public int Kind() { return 8; } }
OK,準備工作做好了, 看我們如何實現。
package com.liruilong; import java.util.logging.Logger; /** * @Project_name: workspack * @Package: com.liruilong * @Description: 孩子類 * @Author: [email protected] * @WeChat_Official_Accounts: 山河已無恙 * @blog: https://liruilong.blog.csdn.net/ * @Date: 2022/2/12 13:16 */ public class Son extends FatherImpl implements Mother { static Logger logger = Logger.getAnonymousLogger(); MotherSpecial motherSpecial = new MotherSpecial(); @Override public int strong() { return super.strong() + 1; } @Override public int Kind() { return motherSpecial.Kind(); } @Override public String toString() { return "Son{" + "height=" + height +"," + "pretty=" + MotherSpecial.pretty + '}'; } public class MotherSpecial extends MotherImpl { @Override public int Kind() { return super.Kind() - 1; } } public static void main(String[] args) { Son son = new Son(); logger.info(son.toString()); logger.info(son.strong()+""); logger.info(son.Kind()+""); } }
我們用內部類繼承一個外部類無關的類,實現了Son類的多繼承
Bad level value for property: .level Bad level value for property: java.util.logging.ConsoleHandler.level Can''t set level for java.util.logging.ConsoleHandler 二月 12, 2022 2:02:06 下午 com.liruilong.Son main 資訊: Son{height=身體超高,pretty=臉蛋特別漂亮} 二月 12, 2022 2:02:06 下午 com.liruilong.Son main 資訊: 9 二月 12, 2022 2:02:06 下午 com.liruilong.Son main 資訊: 7 Process finished with exit code 0
這裡只是討論這樣的寫法,我個人認為,這種方法有些雞肋。這種方式實現的多繼承,完全可以通組合的方式來實現,我們簡單分析一下優缺點。
優缺點分析
優點:
通過內部類的方式,把繼承關係控制在類的內部,理論上比通過組合的方式更加安全,程式碼可讀性要好一點。
更符合設計原則中的迪米特法則,又稱最少知道原則(Demeter Principle),一個實體應當儘量少地與其他實體之間發生相互作用,使得系統功能模組相對獨立。
缺點:
首先通過繼承的方式實現,打破了類的封裝性,子類依賴於其超類中特定功能的實現細節。 超類的實現有可能會隨著發行版本的不同而有所變化,如果真的發生了變化,即使子類的程式碼完全沒有改變,但是子類可能會遭到破壞因而,子類必須要跟著其超類的更新而演變,除非超類是專門為了擴充套件而設計的,並且具有很好的文擋說明。
其次,通過這樣的方式實現的,不符合常態思想,尤其內部類同名的情況,容易被忽略某些特性(見JDK原始碼)。
JDK原始碼中的運用
關於通過內部類來實現java多繼承的JDK場景,我們簡單分析一下:
asList
List<Integer> integers = Arrays.asList(1, 2, 3);
這個程式碼小夥伴們一定不陌生,這裡通過Arrays工具類來生成一個List,但是這裡的List並不是真正的ArrayList,而是在Arrays工具類內部定義的一個繼承了AbstractList的靜態內部類ArrayList,這裡java通過內部類的方式巧妙的實現了。
....... @SafeVarargs @SuppressWarnings("varargs") public static <T> List<T> asList(T... a) { return new ArrayList<>(a); } /** * @serial include */ private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable { private static final long serialVersionUID = -2764017481108945198L; private final E[] a; ArrayList(E[] array) { a = Objects.requireNonNull(array); } .................
但是這裡同樣需要注意的是通過內部類實現多繼承要考慮其類的特殊性:
這樣生成的List呼叫add方法會拋不支援的操作的異常,基於Arrays的ArrayList是一個靜態私有內部類,除了Arrays能訪問以外,其他類都不能訪問,正常的ArrayList中add方法是ArrayList父類提供,Arrays的內部類ArrayList沒有覆寫add方法。
下面原始碼為ArrayList靜態內部類實現的個方法。
/** * @serial include */ private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable { private static final long serialVersionUID = -2764017481108945198L; private final E[] a; ArrayList(E[] array) { a = Objects.requireNonNull(array); } @Override public int size() { return a.length; } @Override public Object[] toArray() { return a.clone(); } @Override @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { int size = size(); if (a.length < size) return Arrays.copyOf(this.a, size, (Class<? extends T[]>) a.getClass()); System.arraycopy(this.a, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } @Override public E get(int index) { return a[index]; } @Override public E set(int index, E element) { E oldValue = a[index]; a[index] = element; return oldValue; } @Override public int indexOf(Object o) { E[] a = this.a; if (o == null) { for (int i = 0; i < a.length; i++) if (a[i] == null) return i; } else { for (int i = 0; i < a.length; i++) if (o.equals(a[i])) return i; } return -1; } @Override public boolean contains(Object o) { return indexOf(o) != -1; } @Override public Spliterator<E> spliterator() { return Spliterators.spliterator(a, Spliterator.ORDERED); } @Override public void forEach(Consumer<? super E> action) { Objects.requireNonNull(action); for (E e : a) { action.accept(e); } } @Override public void replaceAll(UnaryOperator<E> operator) { Objects.requireNonNull(operator); E[] a = this.a; for (int i = 0; i < a.length; i++) { a[i] = operator.apply(a[i]); } } @Override public void sort(Comparator<? super E> c) { Arrays.sort(a, c); } }
即沒有實現add和remove方法,所以asList返回的為一個長度不可變的列表,陣列為多長轉換為列表為多長,即不在保持列表動態變長的特性。
subList
嗯,不多講,直接上程式碼
ArrayList arrayList = new ArrayList(); LinkedList linkedList = new LinkedList(); Vector vector = new Vector(); linkedList.subList(2,3); arrayList.subList(2,3); vector.subList(2,3);
List提供一個subList方法,與String的subString有點類似,這裡的List通過subList生成子list方式也是通過內部類繼承方式的多繼承實現的。
當然這裡,具體需要分析,ArrayList和其他List的實現的方式略有不同
ArrayList是自己定義的內部類SubList繼承AbstractList實現的
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { ....... public List<E> subList(int fromIndex, int toIndex) { subListRangeCheck(fromIndex, toIndex, size); return new SubList(this, 0, fromIndex, toIndex); } ..... private class SubList extends AbstractList<E> implements RandomAccess { private final AbstractList<E> parent; private final int parentOffset; private final int offset; int size; .........
LinkedList的subList方法是由AbstractList實現的,它會根據是不是隨機儲存提供不同的實現方法,subList返回的類也是AbstractList的子類SubList。
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { ........ public List<E> subList(int fromIndex, int toIndex) { return (this instanceof RandomAccess ? new RandomAccessSubList<>(this, fromIndex, toIndex) : new SubList<>(this, fromIndex, toIndex)); } class SubList<E> extends AbstractList<E> { ... } class RandomAccessSubList<E> extends SubList<E> implements RandomAccess{ ....... } ........ }
這裡需要注意的是,不管是ArrayList還是LinkedList等其他List,通過SubList內部類生成的List,其所有的方法(get,add,set,remove等)都是在原始列表上操作的,它自身並沒有生成一個數組或是連結串列,也就是子列表只是原列表的一個檢視(View),所有的修改都反映在原列表上。