2011-09-02 46 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

嗯,你是对的!访问超类中声明的超类中的字段时,如果将类型化为子类的变量是编译错误。像上面一样,将它分配给一个临时变量和超类的类型来修复它。这很......很愚蠢,而且不是我经常足以注意到的事情。 –