iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 18
1
Mobile Development

Android Kotlin開發 -小嫩雞的30篇精選筆記系列 第 18

Kotlin : java的靜態成員與特性在Kotlin中該如何使用

  • java的靜態特性:

    1. static關鍵字表示其為靜態成員,是全域通用,且有唯一性。譬如靜態變數、靜態方法、靜態內部類別。
    2. jvm虛擬機啟動後,當一個class被使用到(不一定是實例化)時,該class就會被載入,且一併建立該class中的靜態成員,這些靜態成員一直活著直到程序退出。
    3. 非靜態成員屬於物件,靜態成員屬於類別。 所以我們可以直接透過類別名稱呼叫靜態成員。
    4. 所以你會發現,當你要使用非靜態變數或方法時,你必須先實例化它們所屬的class,才能使用這些變數跟方法。但靜態的變數或方法可以不用實例化class,直接呼叫。

    來看看一個程式片段:

class Ball {
    double radius;
    final double PI = 3.14159;
    ...
}

如果你建立了多個Ball物件,那每個Ball物件都會有自己的radius與PI成員:

不過我們都知道,圓周率其實是個固定的常數,不用每個物件各自擁有,你可以在PI上宣告static,表示它屬於類別:

class Ball {
    double radius;
    static final double PI = 3.141596;
    ...
}

被宣告為static的成員,不會讓個別物件擁有,而是屬於類別,如上定義後,如果建立多個Ball物件,每個Ball物件只會各自擁有radius:

被宣告為static的成員,是將類別名稱作為名稱空間,也就是說,你可以如下取得圓周率:

System.out.println(Ball.PI);

也就是透過類別名稱與.運算子,就可以取得static成員。你也可以將宣告方法為static成員。例如:

class Ball {
    double radius;
    static final double PI = 3.141596;
    static double toRadians(double angdeg) { // 角度轉徑度
        return angdeg * (Ball.PI / 180);
    }
}

被宣告為static的方法,也是將類別名稱作為名稱空間,可以透過類別名稱與.運算子來呼叫static方法:

System.out.println(Ball.toRadians(100));

雖然語法上,也是可以透過物件名稱存取static成員,但非常不建議如此撰寫:

Ball ball = new Ball();
System.out.println(ball.PI);                // 極度不建議
System.out.println(ball.toRadians(100));  // 極度不建議

Java程式設計領域,早就有許多良好命名慣例,沒有遵守會造成溝通與維護的麻煩。以static使用慣例來說,是透過「大寫開頭類別名稱.運算子」來存取,所以只要看到這種寫法,就會知道它是static成員。例如我們一直在用的System.out,沒錯!out就是System擁有的static成員!!

  • 那類別呢?有靜態類別嗎?

java允許我們在一個類裡面定義靜態類。比如內部類(nested class)。把nested class封閉起來的類叫外部類。在java中,我們不能用static修飾頂級類(top level class)。只有內部類可以為static。

靜態內部類和非靜態內部類之間到底有什麼不同呢?下面是兩者間主要的不同。

(1)內部靜態類不需要有指向外部類的引用。但非靜態內部類需要持有對外部類的引用。

(2)非靜態內部類能夠訪問外部類的靜態和非靜態成員。靜態類不能訪問外部類的非靜態成員。他只能訪問外部類的靜態成員。

(3)一個非靜態內部類不能脫離外部類實體被建立。

  • kotlin沒有static,怎麼使用靜態?

在kotlin中需要static時,

  1. 在top level定義。kotlin允許在class外面宣告變數與方法,最終在jvm上面run的時候,這些top level的成員將會放在一個以檔名為命名的 class 中,以靜態成員的身份存在。
  2. 在class內使用 companion object 或object定義。
  • 瞧瞧kotlin中怎麼使用靜態

以下用「建立」來代表當一個變數或類別開始佔有記憶體空間。

class MainActivity : AppCompatActivity() {

        //非靜態變數
        //MainActivity被實例化時才會「建立」
        //實例化了10個MainActivity就有10個變數i
        val i = 1

        //靜態內部類別
        //MainActivity被class loader「建立」時,一併將A「建立」
        //內部成員皆為靜態,將一併被「建立」
         object A{
            const val constA = 1
            val a = 1
        }

        //靜態內部類別
        //MainActivity被class loader「建立」時,一併將B「建立」
        //內部成員皆為靜態,將一併被「建立」
        //與object不同的是,內部成員皆隸屬於外部類別
        companion object B{
            const val constB = 1
            val b = 1
        }

    //靜態內部類別
    //MainActivity被class loader「建立」時一併被「建立」
    //內部成員皆為非靜態
    class C{
        var c1 = 0
    }

    //非靜態內部類別
    //MainActivity被實例化時才會「建立」
    inner class D{
        var d2 = 0
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)


    }


}

-------------------decompile之後----------------

public final class MainActivity extends AppCompatActivity {


   //非靜態變數i
   private final int i = 1;
   public final int getI() {
      return this.i;
   }
   
   
   //object A
    public static final class A {
      public static final int constA = 1;
      private static final int a = 1;
      public static final MainActivity.A INSTANCE;

      public final int getA() {
         return a;
      }
      private A() {
      }
      static {
         MainActivity.A var0 = new MainActivity.A();
         INSTANCE = var0;
         a = 1;
      }
   }
   
   //companion object B
   public static final int constB = 1;
   private static final int b = 1;
   public static final MainActivity.B B = new MainActivity.B((DefaultConstructorMarker)null);
   
   public static final class B {
      public final int getB() {
         return MainActivity.b;
      }

      private B() {
      }

      // $FF: synthetic method
      public B(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
   
   
   

   
   //class C
   public static final class C {
      private int c1;

      public final int getC1() {
         return this.c1;
      }
      public final void setC1(int var1) {
         this.c1 = var1;
      }
   }

   
   //inner class D
   public final class D {
      private int d2;

      public final int getD2() {
         return this.d2;
      }

      public final void setD2(int var1) {
         this.d2 = var1;
      }
   }

   
   
}

你各位應該一清二楚了吧
霸脫如有講錯的地方請趕快糾正我

參考資料:
1.那些kotlin中的靜態事
2.static類別成員
3.Java中靜態內部類和非靜態內部類


上一篇
Kotlin : 單例模式及kotlin object運作原理
下一篇
Android : 淺淺淺談記憶體洩漏Memory leak
系列文
Android Kotlin開發 -小嫩雞的30篇精選筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言