2011-09-02 49 views
3

結合了Java中的CRTP私人領域的使用私人的訪問似乎在可見性規則癢癢一個奇怪的邊緣情況:與自我約束的仿製藥

public abstract class Test<O extends Test<O>> implements Cloneable { 
    private int x = 0; 

    @SuppressWarnings("unchecked") 
    @Override 
    protected final O clone() { 
     try { 
      return (O) super.clone(); 
     } catch (CloneNotSupportedException ex) { 
      throw new AssertionError(ex); 
     } 
    } 

    public final int getX() { 
     return x; 
    } 

    public final O withX(int x) { 
     O created = clone(); 
     created.x = x; // Compiler error: The field Test<O>.x is not visible 
     return created; 
    } 
} 

簡單地改變withX()方法來此...

public final O withX(int x) { 
     O created = clone(); 
     Test<O> temp = created; 
     temp.x = x; 
     return created; 
    } 

...使代碼編譯。我在Oracle的javac和Eclipse的編譯器中對此進行了測試。是什麼賦予了?

+0

可克隆和克隆是用Java中的WTF和陷阱填充的。 – Powerlord

+0

@Powerlord正如下面接受的答案中所記錄的,這個問題與'clone()'無關。 –

回答

7

這實際上是泛型的問題。 JLS inheritance rules使私人字段在子類中不可見。由於X是私有的,它不是O類型的成員,即使它是Test<O>類型的成員,O也是Test<O>的子類型。如果你曾經使用過這樣的代碼:

public final O withX(int x) { 
    Test<O> created = clone(); 
    created.x = x; 
    return (O) created; 
} 

它會工作。這是Java不支持LSP的實例,但它只是類型系統的本地問題,因爲專用字段僅適用於相同類型的對象。如果這種方式不如私人領域真的是私人的。我不認爲遞歸模板的規則有一個特殊的例外是個好主意。

請注意,這絕不是您可以執行的操作的限制。當您想要進行更改時,您始終可以將子類型轉換爲超類型,就像您在替代代碼中一樣。

+0

嗯,你是對的!訪問超類中聲明的超類中的字段時,如果將類型化爲子類的變量是編譯錯誤。像上面一樣,將它分配給一個臨時變量和超類的類型來修復它。這很......很愚蠢,而且不是我經常足以注意到的事情。 –