2013-03-16 26 views
28

有這樣的事情:如何從抽象類擴展枚舉類?

public enum Token 
{ 
    FOO("foo", "f"), 
    QUIT("quit", "q"), 
    UNKNOWN("", ""); 
    ... 

    public parse(String s) { 
     for (Token token : values()) { 
       ... 
       return token; 
     } 
     return UNKNOWN; 
    } 
} 

一個抽象類:

abstract class Base 
{ 
    private boolean run; 

    Base() { 
     run = true; 

     while (run) { 
      inp = getInput(); 
      act(inp); 
     } 
    } 

    public boolean act(String s) { 
     boolean OK = true; 
     switch (Token.parse(inp)) { /* Enum */ 
      case FOO: 
        do_foo(); 
        break; 
      case QUIT: 
        run = false; 
        break; 
      case UNKNOWN: 
        print "Unknown" + inp; 
        OK = false; 
        break; 
      } 
     } 
     return OK; 
    } 
} 

而且擴展:

class Major extends Base 
{ 

} 

我想是在如果super不處理擴展act然後嘗試在Major中處理它。例如。添加PRINT_STAT("print-statistics", "ps") - 但同時讓Base類處理默認值,如QUIT

這是一個完全錯誤的方法?

我迄今所做的通常是添加接口:

public interface BaseFace 
{ 
     public boolean act_other(String inp); 
} 

而且在Base implements BaseFace類:

 case UNKNOWN: 
      OK = act_other(inp); 

而且在Major類:

public boolean act_other(String inp) { 
     if (inp.equals("blah")) { 
      do_blah(); 
      return true; 
     } 
     return false; 
    } 

戴上這個像一個可用的設計?

而且,主要的問題:

是否有延長Token類,這樣我可以在Base使用Major相同的開關方法,因爲一些好的方法嗎?我想知道的是,如果一個是更好的設計,如果我不得不爲Major創建一個新的令牌類,或者如果我以某種方式可以擴展或以其他方式重新使用現有的。


編輯:概念的一點是有Base類,我可以很容易地重新使用在不同的項目處理各種類型的輸入。

回答

39

所有枚舉隱含擴展枚舉。在Java中,一個類最多可以擴展一個其他類。

但是,您可以讓您的枚舉類實現接口

this Java tutorial on Enum Types

注:所有枚舉隱式擴展java.lang.Enum中。由於類只能擴展一個父類(請參見聲明類),所以Java語言不支持多態繼承(請參閱狀態的多重繼承,實現和類型),因此枚舉不能擴展其他任何內容。

編輯爲Java 8:

從Java 8的,接口可以包括default methods。這允許您在接口中包含方法實現(但不包括狀態)。儘管此功能的主要目的是允許公共接口的演變,但您可以使用它繼承定義多個枚舉類之間常見行爲的自定義方法。

但是,這可能是脆弱的。如果具有相同簽名的方法稍後被添加到java.lang.Enum類中,它將覆蓋您的默認方法。 (當一個方法在類的父類和接口定義兩者的類實現總是獲勝。)

例如:

interface IFoo { 
    public default String name() { 
     return "foo"; 
    } 
} 

enum MyEnum implements IFoo { 
    A, B, C 
} 

System.out.println(MyEnum.A.name()); // Prints "A", not "foo" - superclass Enum wins 
+3

有用的鏈接來完成教程:http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.9 – Aubin 2013-03-16 15:25:40

+0

謝謝。我讀了那篇,但顯然沒有抓到所有東西。我會看看界面方法。像你的頭像順便說一句:)。並感謝@Aubin, - 額外的鏈接。還沒有讀過斑點,但看起來像我應該。它看起來像我喜歡的語言。 – Zimzalabim 2013-03-16 15:36:11

+0

在類似的情況下,我想從一個抽象基類繼承非平凡的實現,最後我以一個枚舉作爲實例成員。 – 2013-03-16 15:41:03

2

你需要分解出的接口。畢竟,總是從一個接口開始,然後提供一個抽象類來提供一些默認實現。如果你有一個接口,你可以讓枚舉實現接口。

+0

謝謝我會爲此努力。我對Java很陌生 - 所以這種輸入非常有用。 – Zimzalabim 2013-03-16 15:37:54

3

正如其他人在這裏說的,你不能擴展枚舉。從設計角度來看,這個解決方案看起來好像耦合得太緊密。我建議爲此使用更多動態的方法。您可以創建某種行爲圖:

Map<Token, Runnable> behaviors; 

此地圖可以很容易地修改或替換。你甚至可以存儲一些預定義的行爲。在例如:

behaviors.get(Token.parse(inp)).run(); 

(這裏當然需要一些額外的檢查)

而且最後要注意的:在大多數情況下避免繼承

+0

謝謝。是的,我按照[我以前的Q](http://stackoverflow.com/a/15440238/2148245)查看了'Map'方法。試過了,但是我猜想我錯過了一些東西 - 不是我不想進一步研究它,而是想到路上......現在應該做。 – Zimzalabim 2013-03-16 16:01:42

+0

另外 - 關於*「緊密耦合」*和*「避免繼承」*。我做了一個關於「避免繼承」的快速網絡搜索 - 並討論了這些[爲什麼避免Java繼承「擴展」](http://programmers.stackexchange.com/a/75203)和[爲什麼擴展是邪惡](http ://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html),但我必須多讀幾遍,希望能夠抓住問題的嚴重程度。 – Zimzalabim 2013-03-16 16:08:38

4

您的問題似乎是一個很好的候選人命令模式

將枚舉用作受支持操作的邏輯組是一個很好的做法。國際海事組織,有一個單一枚舉來分組所有支持的行動將提高你的代碼的可讀性。考慮到這一點,在令牌枚舉應包含如下圖所示的所有受支持的動作類型

enum Token 
{ 
    FOO("foo", "do_foo"), 
    QUIT("quit", "do_quit"), 
    PRINT_STATS("print", "do_print_stats"), 
    UNKNOWN("unknown", "unknown") 
    ..... 

} 

考慮創建一個接口演員,它定義了一個方法說的行爲:

public interface Actor 
{ 
    public void act(); 
} 

而是具有一個基地類也可能是事情,你可以有一個類每支持命令爲例如

public class FooActor implements Actor 
{ 
    public void act() 
    { 
     do_foo(); //call some method like do_foo 
    } 
} 

public class PrintActor implements Actor 
{ 
    public void act() 
    { 
     print_stats(); //call some print stats 
    } 
} 

最後,將有一個驅動器的代碼,將通過調用動作()方法採取在作爲輸入的動作被執行,初始化相應演員和執行的操作。

public class Driver 
{ 
    public static void main(String[] args) 
    { 
     String command; // will hold the input string from the user. 

     //fetch input from the user and store it in command 

     Token token = Token.parse(command); 

     switch(token) 
     { 
     case FOO: 
        new FooActor().act(); 
        break; 

     case PRINT_STATS: 
        new PrintActor().act(); 
        break; 
      .... 

     } 


    } 
} 

這樣的設計將確保您可以輕鬆地添加新命令並且代碼保持模塊化。

+0

好點(我猜 - 我在Java只待了幾個星期)。我對這個具體案例的這種方法的問題是,我有**很多**的行爲*通常是50+。不少是單線。這意味着50多個文件和50多個課程,我是否正確?或者我可以偏離課程,只爲那些擁有更龐大代碼基礎的人增加一個班級。更重要的一點是重用相同的基... – Zimzalabim 2013-03-16 16:23:19

+0

你可以添加一個行爲方法的枚舉,而不是開關(令牌)只是調用token.act(); – Dave 2014-04-11 10:59:46

+0

這組命令位於必須同步的三個地方:enum,實現的類和「驅動程序」。在一個地方執行命令並且忘記另一個命令很容易。我可能會讓驅動程序備用,並將實現的Actor的實例放入枚舉器的另一個構造器參數中。 – 2016-11-08 11:34:25