为什么Java不限制子类的可见性大于父类

为什么Java不限制子类的可见性大于父类,java,polymorphism,package,public,overriding,Java,Polymorphism,Package,Public,Overriding,为什么您可以在Java中创建一个子类,其可见性低于超类 package 1 public class Class1 { public Class1 hello(){ Class1 c= new Class2(); return c; } } class Class2 extends Class1 { public Class1 hello() { System.out.println("In override

为什么您可以在Java中创建一个子类,其可见性低于超类

package 1
public class Class1 {

    public Class1 hello(){
        Class1 c= new Class2();
        return c;
    }   
}


class Class2 extends Class1 {

     public Class1 hello() {
        System.out.println("In overriden method");
            return null;
    }    
}
假设Class1和Class2都在同一个包中,Class2可见性是包私有的

package 2
public class Major {
    public static  void main(String[] args) {
        Class1 ob = new Class1();
        ob.hello().hello();
    }
}
您将注意到,由于运行时多态性,将打印“In-overrided方法”

在运行期间,当
Class2
位于具有包私有可见性的不同包中时,如何从
main()
访问
hello()
方法


Class2
不应被访问,因此
Class2
中的
hello(
)方法也不应被访问。

protected关键字(某种程度上类似于package private)是一种保护代码不被外部使用的方法,这正是您的示例中发生的情况。您不是直接使用
Class2
,而是使用
Class1
,它授予您间接使用受保护的
Class2
的权利

这是一项责任原则
public Class1
负责授权外部包使用受保护的Class2。但这一责任也意味着
Class1
的开发人员必须知道为什么以及如何授予您这一权利


在您的示例中,
extend Class1
这一事实没有实际意义,您的困惑可能来自于此。您不能从包外部实例化
Class2
,但
Class1
可以。

protected关键字(某种程度上类似于package private)是一种保护代码不被外部使用的方法,这正是您的示例中发生的情况。您不是直接使用
Class2
,而是使用
Class1
,它授予您间接使用受保护的
Class2
的权利

这是一项责任原则
public Class1
负责授权外部包使用受保护的Class2。但这一责任也意味着
Class1
的开发人员必须知道为什么以及如何授予您这一权利


在您的示例中,
extend Class1
这一事实没有实际意义,您的困惑可能来自于此。您不能从包外实例化
Class2
,但
Class1
可以。

实际上,考虑到所涉及方法的类型签名,它可能无法以任何其他方式工作

Class1
声明一个名为
hello
的方法,返回
Class1
的对象。因此,从该方法返回的内容必须实现与
Class1
兼容的接口。当
Class1.hello()
返回
Class2
的实例时,调用者与该实例的交互方式不应与
Class1
的实例不同-这是实际的-关键的面向对象原则之一

如果
Class2.hello()
由于
Class2
被隐藏而变得不可访问,那么调用者将不得不面对这样一个事实,即他们不知道
Class1.hello()将返回什么类型的对象
-有时它可能是
Class2
的对象,有时它可能是
Class1
的对象,或者可以从
Class1
扩展的任何其他对象,知道的唯一方法是检查代码-即知道
Class1
的实现细节。在这种情况下,编译器将无法保证类型安全,因为
Class1.hello()
原则上可能是不可判定的或依赖于运行时状态。在一种静态类型的语言中,这几乎是游戏结束了


以某种方式隐藏一个类—通过将其创建为私有类型、匿名实现接口、通过反射欺骗等—只会隐藏类型本身,而不会隐藏它所实现的接口。通过扩展另一个类,任何给定类型都会自动继承父类型的接口,包括其可见性。扩展类型只允许增加接口的可见性,而不是降低它,否则就违反了Liskov替换原则。

实际上,考虑到所涉及方法的类型签名,它可能无法以任何其他方式工作

Class1
声明一个名为
hello
的方法,返回
Class1
的对象。因此,从该方法返回的内容必须实现与
Class1
兼容的接口。当
Class1.hello()
返回
Class2
的实例时,调用者与该实例的交互方式不应与
Class1
的实例不同-这是实际的-关键的面向对象原则之一

如果
Class2.hello()
由于
Class2
被隐藏而变得不可访问,那么调用者将不得不面对这样一个事实,即他们不知道
Class1.hello()将返回什么类型的对象
-有时它可能是
Class2
的对象,有时它可能是
Class1
的对象,或者可以从
Class1
扩展的任何其他对象,知道的唯一方法是检查代码-即知道
Class1
的实现细节。在这种情况下,编译器将无法保证类型安全,因为
Class1.hello()
原则上可能是不可判定的或依赖于运行时状态。在一种静态类型的语言中,这几乎是游戏结束了

以某种方式隐藏一个类—通过将其创建为私有类型、匿名实现接口、通过反射欺骗等—只会隐藏类型本身,而不会隐藏内部