匿名類別 Anonymous

      在〈匿名類別 Anonymous〉中尚無留言

匿名物件

在產生一個物件時, 通常會指定類別型態, 變數名稱, 如 Pokemon p1=new Pokemon()

但有時產生物件時, 也懶的給變數名稱, 就會形成了匿名物件

 while(true){
    new Pikachu();
 }

這種寫法在Java是沒問題的, 因為一直產生出來的皮卡丘, 會被垃圾回收機制給刪除, 所以不會造成記憶体不足. 但如果是寫在 C/C++裏, 大約二秒後, 記憶体就不足了, 連滑鼠也動不了.

一般類別

先看如下程式碼

public class App {
    public static void main(String[] args) {
        Pikachu p1=new Pikachu();
        Pikachu p2=new Pikachu();
    }
}
abstract class Pokemon{
    int level;
    abstract void setLevel(int l);
}
class Pikachu extends Pokemon{
    @Override
    void setLevel(int l) {
        this.level=l;
    }
}

Pikachu 繼承了Pokemon, 然後產生了二隻皮卡丘. 所以通常會先寫一個繼承Pokemon的Pikachu類別, 然後再由Pikachu類別產生p1及p2二個物件.

若需要產生一隻超級皮卡丘, 一輩子只會出現一次, 而且通常只是短暫的時光, 完成任務就就死了. 如果也要產生一個類別, 再產生一個物件, 好像有點怪怪的.  這種一輩子只產生一個物件的類別, 就成了匿名類別的重點.

匿名類別

超級皮卡丘的產生, 可由如下藍色的程式碼達成. 先使用父類別參考(Pokemon), 再產生一個匿名的類別, 並實作抽象方法. 記得最後要加上 “;”

public class App {
    public static void main(String[] args) {
    public static void main(String[] args) {
        Pokemon p3=new Pokemon(){
            @Override
            public void setLevel(int l) {
                this.level=l*10000;
            }
        };
    }
}
abstract class Pokemon{
    int level;
    abstract void setLevel(int l);
}
class Pikachu extends Pokemon{
    @Override
    void setLevel(int l) {
        this.level=l;
    }
}

使用父類別參考

由上可知, 產生的新類別並沒有名稱, 那物件的參考該如何決定呢? 當然也只能使用父類別當作參考. 如 Pokemon p3=new Pokemon(){……}

匿名類別建構子

建構子名稱必需與類別名稱一模一樣. 但匿名類別並沒有名稱. 所以建構子當然無法寫出來. 所以如果希望在物件產生時利用建構子初始化, 那麼匿名類別就不適用了.

 Pokemon p3=new Pokemon(){
     public 建構子????(){
 
     }
 };

擴充新方法

匿名類別使用了父類別參考, 所以如果在匿名類別中加了擴充方法後, 雖然編譯不會出錯, 但確無法使用. 如下所示, p3.lighting()是錯誤的. 不要忘了, 使用父類別參考子類別物件時, 子類別的擴充功能是會失效的. 

所以, 在匿名類別中, 通常不會再加入新的方法

    Pokemon p3=new Pokemon(){
        @Override
        public void setLevel(int l) {
            this.level=l*10000;
        }
        public void lighting(){
            System.out.println("一萬伏特閃電攻擊");
        }
    };
    //p3.lighting(); error

繼承抽象類別的匿名類別

抽象類別是無法產生物件的, 所以依上面程式碼, Pokemon p=new Pokemon(); 是一個錯誤的寫法.

但如果是Pokemon p=new Pokemon(){….}; 則是產生一個沒有名字的類別, 然後再由此匿名類別產生物件.

繼承一般類別的匿名類別

乍看之下,  匿名類別好像都是繼承了抽象類別而來的, 但其實不然, 匿名類別也可以繼承一般類別. 

public class App {
    public static void main(String[] args) {
        Pikachu p4=new Pikachu(){
            //無需作任何事
            //也可以覆蓋setLevel()方法
        };
    }
}
abstract class Pokemon{
    int level;
    abstract void setLevel(int l);
}
class Pikachu extends Pokemon{
    @Override
    void setLevel(int l) {
        this.level=l;
    }
}

實作介面的匿名類別

介面一樣不能產生物件. 但卻也可以由介面產生新的匿名類別, 再由此匿名類別產生物件

public class App {
    public static void main(String[] args) {
        Land p5=new Land(){
            @Override
            public void setWalk() {
                System.out.println("時速為5公里");
            }
        };
        p5.setWalk();
    }
}
interface Land{
    void setWalk();
}

繼承類別再實作介面的匿名類別

假如有一個類別如下

class Eve extends Pokemon implements Land()

那麼是否上面的Eve也可以使用匿名類別來完成呢? 目前是不可能. 不過, Oracle改天更新版本又想出了一些怪招的話, 這也說不一定.

編譯的檔名

上面的程式碼中, 在檔案總管裏可以看出, 每個類別都會編譯出class檔, 如App.class, Pokemon.class, Pikachu.class.

注意喔, 連介面 interface Land{} 都會產生出一個Land.class. 所以由此可知, Java好像也把介面當成一個類別在看待

那匿名類別呢? 則產生出 App$1.class. $後面那個 “1”, 則是流水編號

Runnable

多執行緒的介紹, 將於另一篇章節中詳細說明, 在此僅說明匿名類別的應用

多執行緒的Runnable, 是一個介面, 系統就已經幫我們寫好了, 所以我們不需要撰寫. 裏面的程式碼如下

interface Runnable{
    public void run();
}

當需要使用Runnable物件時, 可由如下方式執行.

    Thread t1=new Thread(new Runnable(){
        @Override
        public void run() {
            for (int i=0;i<100;i++){
                System.out.printf("%s:%d\n", Thread.currentThread().getName(), i);
            }
        }
    });

匿名Thread物件啟動 start()

當我們產生一個Thread物件 t1, 必需使用t1.start()來啟動新的執行緒. 但當我們使用匿名物件時, 那要如何啟動呢? xxx.start();

此時可以在產生匿名物件後直接start(),
new Thread(new Runnable(){…….}).start(); 如下所說

    new Thread(new Runnable(){
        @Override
        public void run() {
            for (int i=0;i<100;i++){
                System.out.printf("%s:%d\n", Thread.currentThread().getName(), i);
            }
        }
    }).start();

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *