Java Interface,abstract class和enum的比對和組合使用方法

java Interface應該是爲了實現多繼承而添加的,提到Interface,那就不得不說abstract class,的確abstract class和Interface有很多地方很相似,例如都是抽象,都不能實例化,implements或者extends需要實現裏面全部的abstract函數。但是他們的區別也很多。
區別:
1 Interface中的函數默認屬性是public和abstract(無論是否在code中明確寫出來),函數不能有body;而abstract class的默認屬性應該是protected的,如果屬性不是abstract,那就需要body或者是abstract屬性。
2 Interface可以implements多個,而abstract只能extends一個。
3 abstract class還是class,從編譯後的class文件中,仍然能夠看到默認的構造函數,而interface沒有。
4 Interface中的成員變量默認是public static final的,也就是常量,所以不能定義可變量,但是abstract class除了定義常量還可以定義可變量。如果能夠選用interface,就最好用interface。另外如果文件中大量使用常量,並且不同文件中會使用同一個,那麼可以將使用到的所有常量定義在一個interface裏面,不需要定義函數,也不要使用一段時間後定義函數,之後各個使用的類直接implements interface就可以了,或者import interface也可以。
例如:

public interface IColor {
    public static final int RED = 0;
    public static final int BLUE = 1;
    public static final int GREEN = 2;
}
public class Cloth implements IColor {
    protected int computePrice(int color) {
        if (color == RED) {
            return 100;
        } else {
            return 50;
        }
    }
}

當然也可以用import interface的方式了,這樣就無論interface中是否有定義函數,都可以使用裏面的常量。
例如:

public interface IColor {
    public static final int RED = 0;
    public static final int BLUE = 1;
    public static final int GREEN = 2;
    void setColor();
}

import Icolor;
public class Cloth { //這裏如果寫成public class Cloth implements IColor ,那麼就必須實現setColor
    protected int computePrice(int color) {
        if (color == IColor.RED) {
            return 100;
        } else {
            return 50;
        }
    }
}

interface還可以嵌入到類裏面,將常量根據類別進行分組,使得對外使用這些常量更好的組織在一起。
例如

public class Car {
    int mClor;
    int mSize;
    public interface Size {
        public static final int BIG = 0;
        public static fianl int SMALL = 1;
    }
    
    public interface SkinColor {
        public static final int RED = 0;
        public static final int BLUE = 1;
    }
    
    public int setColor (int color) {
        mClor = color;
    }

    public int setSize (int size) {
        mSize = size;
    }
}

import Car;
public class CarShop {
    Car newCar = new Car();
    newCar.setColor(Car.SkinColor.RED); // 這裏不能寫成newCar.setColor(newCar.SkinColor.RED)
    newCar.setColor(Car.Size.BIG);
}

當然用interface當enum使用,不如enum安全了,例如setColor的參數是int,除非setColor裏面判斷傳入參數的有效性。
例如

public void setColor (int color) {
    if (color < RED || color > BLUE) {
        return -1;
    }
    mClor = color;
    return mClor;
}

既然提到了enum,我們就研究一下,Java enum的功能比較強大,使用也比較方便。

package son;
public enum TestEnum {
    ALWAYS_USE,
    ASK_EVERY_TIME,
    NEVER_USE;
    public void set(TestEnum value) {
        System.out.println(value);
    }
}

import son.TestEnum;
public class Test {
    public TestEnum returnEnumValue(TestEnum value) { // 這就比使用interface的static的value安全了,因爲參數現在只能傳入TestEnum
        return value;
    }
    
    public static void main(String[] args) [
        Test test = new Test();
        System.out.println(test.returnEnumValue(TestEnum.ALWAYS_USE));
        System.out.println(TestEnum.valueOf("ALWAYS_USE"));
    }
}

我們可以通過Java document或者網上資源來了解獲取新知識,但是對於Java我們可以先看一下編譯出來的class文件,再結合網絡資源,可以說事半功倍。下面就是TestEnum編譯出來後的class文件。從class文件可以看出TestEnum繼承了java.lang.Enum,TestEnum的屬性是public final的class,也就是說TestEnum不能被繼承並且也是一個class,而其中的enum value值是public static final son.TestEnum的屬性,另外還有兩個static的函數values()和valueOf,values返回TestEnum中的所有值,而valueOf的參數是String,通過String返回TestEnum對應的值。關於valueOf需要注意傳入的String如果不是ENUM裏面能夠對應的string值,編譯的時候不會有問題,但是執行的時候會有異常。之後在文件的最後有static code,從語義上來說應該是將Enum對應的value值“ALWAYS_USE”,“ASK_EVERY_TIME”,“NEVER_USE”,放到一個地方存起來,這部分應該和父類Enum相關,具體的含義需要參考Enum的實現了。
從class文件我們就瞭解了Enum的知識,Enum是一個public final class,裏面有兩個static的函數values()和valueOf(),並且enum value值是public static final的屬性。

  Compiled from "TestEnum.java"
public final class son.TestEnum extends java.lang.Enum<son.TestEnum>
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
Constant pool:
   #1 = Fieldref           #4.#39         // son/TestEnum.$VALUES:[Lson/TestEnum;
   #2 = Methodref          #40.#41        // "[Lson/TestEnum;".clone:()Ljava/lang/Object;
   #3 = Class              #22            // "[Lson/TestEnum;"
   #4 = Class              #42            // son/TestEnum
   #5 = Methodref          #16.#43        // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
   #6 = Methodref          #16.#44        // java/lang/Enum."<init>":(Ljava/lang/String;I)V
   #7 = Fieldref           #45.#46        // java/lang/System.out:Ljava/io/PrintStream;
   #8 = Methodref          #47.#48        // java/io/PrintStream.println:(Ljava/lang/Object;)V
   #9 = String             #17            // ALWAYS_USE
  #10 = Methodref          #4.#44         // son/TestEnum."<init>":(Ljava/lang/String;I)V
  #11 = Fieldref           #4.#49         // son/TestEnum.ALWAYS_USE:Lson/TestEnum;
  #12 = String             #19            // ASK_EVERY_TIME
  #13 = Fieldref           #4.#50         // son/TestEnum.ASK_EVERY_TIME:Lson/TestEnum;
  #14 = String             #20            // NEVER_USE
  #15 = Fieldref           #4.#51         // son/TestEnum.NEVER_USE:Lson/TestEnum;
  #16 = Class              #52            // java/lang/Enum
  #17 = Utf8               ALWAYS_USE
  #18 = Utf8               Lson/TestEnum;
  #19 = Utf8               ASK_EVERY_TIME
  #20 = Utf8               NEVER_USE
  #21 = Utf8               $VALUES
  #22 = Utf8               [Lson/TestEnum;
  #23 = Utf8               values
  #24 = Utf8               ()[Lson/TestEnum;
  #25 = Utf8               Code
  #26 = Utf8               LineNumberTable
  #27 = Utf8               valueOf
  #28 = Utf8               (Ljava/lang/String;)Lson/TestEnum;
  #29 = Utf8               <init>
  #30 = Utf8               (Ljava/lang/String;I)V
  #31 = Utf8               Signature
  #32 = Utf8               ()V
  #33 = Utf8               set
  #34 = Utf8               (Lson/TestEnum;)V
  #35 = Utf8               <clinit>
  #36 = Utf8               Ljava/lang/Enum<Lson/TestEnum;>;
  #37 = Utf8               SourceFile
  #38 = Utf8               TestEnum.java
  #39 = NameAndType        #21:#22        // $VALUES:[Lson/TestEnum;
  #40 = Class              #22            // "[Lson/TestEnum;"
  #41 = NameAndType        #53:#54        // clone:()Ljava/lang/Object;
  #42 = Utf8               son/TestEnum
  #43 = NameAndType        #27:#55        // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
  #44 = NameAndType        #29:#30        // "<init>":(Ljava/lang/String;I)V
  #45 = Class              #56            // java/lang/System
  #46 = NameAndType        #57:#58        // out:Ljava/io/PrintStream;
  #47 = Class              #59            // java/io/PrintStream
  #48 = NameAndType        #60:#61        // println:(Ljava/lang/Object;)V
  #49 = NameAndType        #17:#18        // ALWAYS_USE:Lson/TestEnum;
  #50 = NameAndType        #19:#18        // ASK_EVERY_TIME:Lson/TestEnum;
  #51 = NameAndType        #20:#18        // NEVER_USE:Lson/TestEnum;
  #52 = Utf8               java/lang/Enum
  #53 = Utf8               clone
  #54 = Utf8               ()Ljava/lang/Object;
  #55 = Utf8               (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
  #56 = Utf8               java/lang/System
  #57 = Utf8               out
  #58 = Utf8               Ljava/io/PrintStream;
  #59 = Utf8               java/io/PrintStream
  #60 = Utf8               println
  #61 = Utf8               (Ljava/lang/Object;)V
{
  public static final son.TestEnum ALWAYS_USE;
    descriptor: Lson/TestEnum;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final son.TestEnum ASK_EVERY_TIME;
    descriptor: Lson/TestEnum;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final son.TestEnum NEVER_USE;
    descriptor: Lson/TestEnum;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static son.TestEnum[] values();
    descriptor: ()[Lson/TestEnum;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #1                  // Field $VALUES:[Lson/TestEnum;
         3: invokevirtual #2                  // Method "[Lson/TestEnum;".clone:()Ljava/lang/Object;
         6: checkcast     #3                  // class "[Lson/TestEnum;"
         9: areturn
      LineNumberTable:
        line 3: 0

  public static son.TestEnum valueOf(java.lang.String);
    descriptor: (Ljava/lang/String;)Lson/TestEnum;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: ldc           #4                  // class son/TestEnum
         2: aload_0
         3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
         6: checkcast     #4                  // class son/TestEnum
         9: areturn
      LineNumberTable:
        line 3: 0

  public void set(son.TestEnum);
    descriptor: (Lson/TestEnum;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: aload_1
         4: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
         7: return
      LineNumberTable:
        line 9: 0
        line 10: 7

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=4, locals=0, args_size=0
         0: new           #4                  // class son/TestEnum
         3: dup
         4: ldc           #9                  // String ALWAYS_USE
         6: iconst_0
         7: invokespecial #10                 // Method "<init>":(Ljava/lang/String;I)V
        10: putstatic     #11                 // Field ALWAYS_USE:Lson/TestEnum;
        13: new           #4                  // class son/TestEnum
        16: dup
        17: ldc           #12                 // String ASK_EVERY_TIME
        19: iconst_1
        20: invokespecial #10                 // Method "<init>":(Ljava/lang/String;I)V
        23: putstatic     #13                 // Field ASK_EVERY_TIME:Lson/TestEnum;
        26: new           #4                  // class son/TestEnum
        29: dup
        30: ldc           #14                 // String NEVER_USE
        32: iconst_2
        33: invokespecial #10                 // Method "<init>":(Ljava/lang/String;I)V
        36: putstatic     #15                 // Field NEVER_USE:Lson/TestEnum;
        39: iconst_3
        40: anewarray     #4                  // class son/TestEnum
        43: dup
        44: iconst_0
        45: getstatic     #11                 // Field ALWAYS_USE:Lson/TestEnum;
        48: aastore
        49: dup
        50: iconst_1
        51: getstatic     #13                 // Field ASK_EVERY_TIME:Lson/TestEnum;
        54: aastore
        55: dup
        56: iconst_2
        57: getstatic     #15                 // Field NEVER_USE:Lson/TestEnum;
        60: aastore
        61: putstatic     #1                  // Field $VALUES:[Lson/TestEnum;
        64: return
      LineNumberTable:
        line 4: 0
        line 5: 13
        line 6: 26
        line 3: 39
}
Signature: #36                          // Ljava/lang/Enum<Lson/TestEnum;>;
SourceFile: "TestEnum.java"

當然interface和enum進行結合使用,在interface中定義enum,可以將相關的常數變量group到一起。
例如

public interface IInnerTestEnum {
    public enum ColorEnum {
        RED,
        BLUE,
        GREEN
    }
}

其他class如果想使用IInnerTestEnum.ColorEnum,可以import IInnerTestEnum,當然也可以通過implements IInnerTestEnum方式使用。
我個人的建議,優先使用interface,之後根據情況選用abstract class或者enum。另外期望大家通過Java的class文件進行學習,這樣不僅深刻而且深入,對Java的機制瞭解更清楚。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章