具有较低可见性的覆盖方法会导致编译错误

时间:2013-12-19 08:12:47

标签: java method-overriding

我有一个班级和一个界面:

public interface A {
    public void getNum();
}

public class B {
    public void getNum() {
        System.out.println("4");
    }
}

public class C extends B implements A {
    protected void getNum() {
        System.out.println("3");
    }
}

现在我的问题是,为什么这段代码会出现编译错误,我们怎么能避免它。我们有什么方法可以在C类中覆盖这个方法吗?

8 个答案:

答案 0 :(得分:13)

来自Java语言规范:

jls-8.4.8.3

  

覆盖或隐藏方法的访问修饰符(第6.6节)必须至少提供与重写或隐藏方法一样多的访问权限,如下所示:

     
      
  • 如果被覆盖或隐藏的方法是公开的,那么覆盖或隐藏方法必须是公开的;否则,发生编译时错误。
  •   
  • ...
  •   

请注意,您尝试覆盖public方法getNum()继承自类B(以及界面A)的新方法protected访问修饰符。这意味着您正在尝试降低此方法的可见性,这种方法根据规范不正确 为了能够覆盖此方法,您需要将public访问修饰符与该方法的新版本一起使用。


为什么你不能降低能见度?看一下使用你的类的下面的代码,但是放在其他的包中并问自己“这段代码应该怎么样?”。

package my.pckage;

import your.pckage.A;
import your.pckage.C;

public class Test{
    public static void main (String[] args){
        C C = new C();
        c.getNum();// ERROR: Test class doesn't have access to `c`s protected method.
                   // Why should it have, Test doesn't extend C.

        A a = (A)c;// Lets try using other reference
        a.getNum();// Should `a` have access to method that is protected in `C`?
                   // If yes, then what is the point of declaring this method 
                   // protected if all I would have to do to get access to it is 
                   // casting instance of C to A interface?
    }
}

答案 1 :(得分:6)

修复拼写错误,然后重试;)

public interface A {
    public void getNum();
}

public class B {
    protected void getNum() {
        System.out.println("4");
    }
}

public class C extends B implements A {
    public void getNum() {
        System.out.println("3");
    }
}

答案 2 :(得分:6)

首先,当你在Java中重写方法时,范围应该从低到高。子类方法的范围应该高于超级类,例如

有效改写

class B {
    protected void getNum() {
        System.out.println("4");
    }

 class C extends B  {
    public void getNum() {
        System.out.println("3");
    }

InValid Overriding

class B {
    public void getNum() {
        System.out.println("4");
    }

class C extends B  {
    protected void getNum() {
        System.out.println("3");
    }

你的第二个问题是你创建了两个无效的公共类,你只能在java文件中创建一个公共类。

答案 3 :(得分:5)

实现接口时,需要强制覆盖它以提供函数的具体实现(除非实现接口的类是抽象的)。在您的情况下,您正在实现一个使您实现getNum()函数的接口,并且由于覆盖了类,您将拥有另一个具有相同签名的函数,这是不允许的。所以你得到了编译错误。

可能的解决方案:您可以将B作为界面。

答案 4 :(得分:2)

Pshemo的解释完全正确,您无法降低overriddeninterface功能的可见性。
让我们来看看

 class B 
 {
    protected void getProtected1() 
    {
        System.out.println("4");
    }

    protected void getProtected2() 
    {
        System.out.println("4");
    }

    public void getPublic1() 
    {
        System.out.println("4");
    }

    public void getPublic2() 
    {
        System.out.println("4");
    }

}

class C extends B
{
    @Override
    private void getPublic1() //COMPILATION ERROR : Cannot reduce the visibility of the inherited method from myzeromqApp.B
    {
        System.out.println("3");
    }

    @Override
    protected void getPublic2() //COMPILATION ERROR :Cannot reduce the visibility of the inherited method from myzeromqApp.B
    {
        System.out.println("3");
    }

    @Override
    private void getProtected1() //COMPILATION ERROR : Cannot reduce the visibility of the inherited method from myzeromqApp.B
    {
        System.out.println("3");
    }

    @Override
    public void getProtected2() // NO ERROR IT MEANS YOU ARE ALLOWED TO INCREASE THE VISIBILITY
    {
        System.out.println("3");
    }       
}

从上面的示例可以看出,在任何情况下都不允许降低功能的可见性。

在你的问题中,你正在尝试实现interface函数,我们知道Java中的interface有规则,

  • 方法:仅限public&允许abstract
  • 字段:(变量)仅publicstatic&允许final

作为规则的大拇指,您永远不会降低overridden或已实施的方法或变量的可见度,对于interface,它始终为public(如果涉及可见性),那么应该在实施的类中始终为public

答案 5 :(得分:2)

如上所述,每个文件只能使用一个公共类。因此,要将它们全部公开,必须创建三个单独的 .java 文件。我将在下面编写代码,并详细说明如何在每种情况下覆盖方法以使用它的正确版本。

可能总是有相同名称的方法,但是为了覆盖,它们必须具有不同的参数列表。这是编译器错误之一,您有三个具有相同参数列表的方法,即无。您可以使用正确的参数列表创建并调用该方法,以获得所需的结果。

A.java:

package stackOverflow.tests; // Sample package for visibility

public Interface A {
    public void getNum(int a); // Method takes a single integer argument
}

B.java:

package stackOverflow.tests;

public class B {
    protected void getNum(int a, int b) { // Method takes two integer arguments, differing in the argument list but equal in name
        System.out.println("4");
    }
}

C.java:

package stackOverflow.tests;

import stackOverflow.tests.A; // Importing both classes to use their methods
import stackOverflow.tests.B;

public class C extends B implements A {
    public void getNum(int a, String x) { // Takes an integer and a string argument
            System.out.println("3");    
    }    

    public void getNum(int a) {
        //Do nothing, as in A.java, this code is necessary to be able to override the method.
    }

    public static void main(String[] arguments) { // Sample main method for implementation
        C c = new C(); // Instantiating class C
        int test = 0; // Initializing two integer variables and one String variable
        int test2 = 0;
        String test3 = "";
        c.getNum(test); // takes one integer, using getNum() from A.java
        c.getNum(test, test2); // takes two integers, using getNum() from B.java
        c.getNum(test, test3); // takes an integer and a String, using getNum() from C.java
    }
}

输出:

4
3

如上面的代码所示,参数列表定义了使用该方法的哪个版本。作为旁注,定义getNum(int a)getNum(int b)没有区别,因此这会导致它无法编译。

答案 6 :(得分:1)

为了实现这一点,您可以执行类似的操作,因为每个文件只能有一个公共类,文件名应该与类的名称相同

public class HelloWorld{

     public static void main(String []args){
        C obj=new C();
        obj.getNum();
     }
}
//interface
interface A {
    public void getNum();
}

 class B {
    protected void getNum() {
        System.out.println("4");
    }
}

 class C extends B implements A {
    public void getNum() {
        System.out.println("3");
    }
}

<强>输出: 3

答案 7 :(得分:1)

java类文件只能有一个公共类或接口。 将接口和已定义类的可见性更改为默认级别,或将其声明为单独的文件。

只有public和abstract修饰符可以应用于接口方法。实现接口的类不能改变方法的可见性(我们不能将它从public更改为protected)。