覆盖方法是否可以使用与基类中不同的访问说明符?

时间:2016-03-12 10:02:03

标签: java abstract modifiers

在抽象类中,哪个访问修饰符必须用于方法, 所以子类可以决定它是否应该公开?有可能"覆盖" Java中的修饰符还是没有?

public abstract class A {

    ??? void method();
}

public class B extends A {
    @Override
    public void method(){
        // TODO
    }
}

public class C extends B {
    @Override
    private void method(){
        // TODO
    }
}

我知道静态绑定会有问题,如果 有人打来电话:

// Will work
A foo = new B()
foo.method();

// Compiler ?
A foo = new C();
foo.method();

但也许有另一种方式。我怎么能做到这一点?

6 个答案:

答案 0 :(得分:12)

可以放宽限制,但不要限制更多:

public abstract class A {
    protected void method();
}

public class B extends A {
    @Override
    public void method(){    // OK
    }
}

public class C extends A {
    @Override
    private void method(){    // not allowed
    }
}

使原始方法private也不起作用,因为这种方法在子类中不可见,因此无法覆盖。

我建议使用interface来有选择地公开或隐藏方法:

public interface WithMethod {
    // other methods
    void method();
}

public interface WithoutMethod {
    // other methods
    // no 'method()'
}

public abstract class A {
    protected void method();
}

public class B extends A implements WithMethod {
    @Override
    public void method(){
      //TODO
    }
}

public class C extends B implements WithoutMethod {
    // no 'method()'
}

...然后只通过接口处理实例。

答案 1 :(得分:8)

覆盖方法时,只能将修改器更改为更宽,而不是相反。例如,此代码有效:

public abstract class A {

    protected void method();
}

public class B extends A {
    @Override
    public void method() { }
}

但是,如果您尝试缩小可见性范围,则会出现编译时错误:

public abstract class A {
    protected void method();
}

public class B extends A {
    @Override
    private void method() {}
}

对于您的情况,我建议C不实施A,因为A的抽象暗示那里& #39;非私人method()

public class C {
    private void method(){
      //TODO
    }
}

另一种选择是使method()中的C实现抛出RuntimeException:

public class C extends A {

    @Override
    public void method(){
        throw new UnsupportedOperationException("C doesn't support callbacks to method()");
    }
}

答案 2 :(得分:6)

出于很好的理由,你所要求的是不可能的。

Liskov substitution principle基本上说:一个类S只是另一个类T的子类,当你可以替换某些" T对象"与一些" S对象" - 没有注意到。

如果您允许S将公共方法简化为私有,那么您就不能再这样做了。因为突然之间,在使用某些T ...时可以调用的方法在S上不再可用。

长话短说:继承不是简单地从天而降的东西。这是程序员负责的类的属性。换句话说:继承意味着不只是写下来而且#34; S类扩展T"在您的源代码中!

答案 3 :(得分:4)

由于多态性,这是不可能的。考虑以下。您拥有类SetEnvIf remote_addr ^xxx.xxx.x.xxx$ block=1 Order allow,deny allow from all deny from env=block 中的方法,其中一些访问修饰符不是A。为什么不私人?因为如果它是私人的,那么其他任何阶级都不会知道它的存在。所以它必须是其他东西,并且必须可以从某处访问其他内容。

现在让我们假设您将类private的实例传递到某个地方。但是你事先将它上传到C,所以你最终将代码放在某处

A

如何解决这个问题的一个很好的例子是Qt中的QSaveFile。与Java不同,C ++实际上允许降低访问权限。所以他们就这样做了,禁止使用void somewhereMethod(A instance) { instance.method(); // Ouch! Calling a private method on class C. } 方法。他们最终得到的是close()子类,它不再是QIODevice。如果您将指向QIODevice的指针传递给某个接受QSaveFile的方法,他们仍然可以调用QIODevice*,因为它在close()中已公开。他们通过调用QIODevice(私有)调用QSaveFile::close()来“修复”这个问题,所以如果你这样做,你的程序会立即崩溃。不是一个非常好的“解决方案”,但没有更好的解决方案。它只是糟糕的OO设计的一个例子。这就是为什么Java不允许它。

修改

并不是说我错过了你的课程是抽象的,但我也错过了abort()而不是B extends C的事实。这样你想做的事情是完全不可能的。如果B中的方法为A,则它也将在所有子类中公开。您唯一能做的就是记录它不应该被称为可能会覆盖它以抛出public。但这会导致与UnsupportedOperationException相同的问题。请记住,您班级的用户可能甚至不知道它是QSaveFile的一个实例,因此他们甚至没有机会阅读其文档。

总的来说,这只是一个非常糟糕的想法。也许你应该问一个关于你试图用这个层次结构解决的确切问题的另一个问题,也许你会得到一些关于如何正确地做到这一点的正确建议。

答案 4 :(得分:3)

以下是@Override合同的一部分。

答案是:没有任何可能实现你的目标。

  

访问级别不能比被覆盖的更严格   方法的访问级别。例如:如果超类方法是   声明公共然后子类中的重写方法不能   无论是私人的还是受保护的。

这不是关于abstract类的问题,而是所有类和方法。

答案 5 :(得分:3)

<强>理论值:

您有确定的修饰符顺序:

public <- protected <- default-access X<- private

覆盖方法时,可以增加但不减小修改器级别。例如,

public -> []
protected -> [public]
default-access -> [public, default-access]
private -> []

<强>实践:

在您的情况下,您无法将???转换为某个修饰符,因为private 是最低的修饰符,而private类成员不会被继承。