为什么外部Java类可以访问内部类的私有成员?


177

我观察到外部类可以访问内部类的私有实例变量。这怎么可能?这是演示相同代码的示例代码:

class ABC{
    class XYZ{
        private int x=10;
    }

    public static void main(String... args){
        ABC.XYZ xx = new ABC().new XYZ();
        System.out.println("Hello :: "+xx.x); ///Why is this allowed??
    }
}

为什么允许这种行为?


这个问题困惑了我相当长一段时间,直到我看到评论...这就是为什么我可以在我的机器不能上网XX.X ..
王胜

4
这些注释使我感到困惑,我在Java 8中运行了上面的代码,它确实可以编译并运行。可以访问xx.x。
莱昂

哈里斯(Harish),您能否不接受已接受的答案(该答案不能回答您提出的问题),而是接受下方的马丁·安德森(Martin Andersson)的答案,该答案可以非常彻底地回答?
临时用户名

FWIW这种语法绝对可怕:new ABC().new XYZ()
Josh M.

Answers:


79

内部类只是一种将真正属于原始外部类的功能完全分开的方法。当您有两个要求时,可以使用它们:

  1. 如果是在单独的类中实现的,那么外部类中的某些功能将最为清晰。
  2. 即使在单独的类中,该功能也与外部类的工作方式密切相关。

鉴于这些要求,内部类可以完全访问其外部类。由于它们基本上是外部类的成员,因此可以访问外部类的方法和属性(包括私有类)是有意义的。


217
这个答案解释了为什么嵌套类可以访问其外部类的私有成员。但是问题是,为什么外部类可以访问嵌套类的私有成员。
安德鲁(Andrew)

13
只需在“给定这些要求 ”之后添加“反之亦然” ,内部类就可以完全访问其外部类,并且现在它可以回答问题。
anthropomo

13
这个人是不是正确回答这个问题,在这里做:stackoverflow.com/questions/19747812/...
科林苏

4
@anthropomo:不,不是。这两个要求都是完全可行的,而外部类则无权访问内部类的私有成员。
或Mapper 2014年

其中一个特别有用的很好的例子是Builder Pattern,stackoverflow.com/a/1953567/1262000。父类只需要一个构造函数,该构造函数接受一个Builder并访问其所有成员变量。否则,您将需要在带有所有私有成员变量的父类中拥有一个私有构造函数。
vikky.rk,2015年

62

如果您想隐藏内部类的私有成员,则可以使用公共成员定义一个Interface并创建一个实现该接口的匿名内部类。下面是示例:

class ABC{
    private interface MyInterface{
         void printInt();
    }

    private static MyInterface mMember = new MyInterface(){
        private int x=10;

        public void printInt(){
            System.out.println(String.valueOf(x));
        }
    };

    public static void main(String... args){
        System.out.println("Hello :: "+mMember.x); ///not allowed
        mMember.printInt(); // allowed
    }
}

这是一段精彩的代码片段。正是我所需要的。谢谢!
kevinarpe13年

请提供可以运行的代码。更何况是不允许私有变量访问的原因。
androidyue 2014年

7
但是然后...内部类是匿名的。你无法创建内部类的几个实例,或使用任何变量声明内部类等
映射器

@OR Mapper,这就是为什么即使在x此处公开,也不允许like的原因mMember.x
MAC

53

内部类(出于访问控制目的)被视为包含类的一部分。这意味着可以完全访问所有私人用户。

实现此方法的方式是使用合成的受程序包保护的方法:内部类将被编译为同一程序包(ABC $ XYZ)中的单独类。JVM不直接支持此级别的隔离,因此在字节码级别,ABC $ XYZ将具有受包保护的方法,外部类将使用这些方法来访问私有方法/字段。



5

内部类的恕我直言重要的用例是工厂模式。封闭类可以准备不带访问限制的内部类的实例,并将该实例传递给外部世界,在此处将尊重私有访问。

abyx声明类静态相反,它不会更改对封闭类的访问限制,如下所示。同样,在同一封闭类中的静态类之间的访问限制也在起作用。我很惊讶 ...

class MyPrivates {
    static class Inner1 { private int test1 = 2; }
    static class Inner2 { private int test2 = new Inner1().test1; }

    public static void main(String[] args) {
        System.out.println("Inner : "+new Inner2().test2);
    }
}

1
很好的评论,但没有答案。
ceving 2013年

@cerving实际上,这是唯一的答案,它使我得以实际了解这个奇怪的设计决策的实际用法。问题是为什么要这样决定,这是一个很好的理由-说明您可能希望内部类访问外部类与希望其他不相关的类访问之间的区别。
et_l 16/09/29

3

访问限制是按类别进行的。类中声明的方法无法访问所有实例/类成员。这是有理由的理由,内部类也可以不受限制地访问外部类的成员,而外部类可以不受限制地访问内部类的成员。

通过将一个类放到另一个类中,就可以使它与实现紧密地联系在一起,并且实现中的任何内容都应该可以访问其他部分。


3

内部类背后的逻辑是,如果您在外部类中创建内部类,那是因为它们将需要共享一些东西,因此它们具有比“常规”类更大的灵活性是有意义的。

如果在您的情况下,让这些类无法查看彼此的内部工作原理没有意义-这基本上意味着该内部类可以简单地成为常规类,则可以将内部类声明为static class XYZ。使用static将意味着它们将不会共享状态(例如,new ABC().new XYZ()将无法使用状态,并且您将需要使用new ABC.XYZ()。)
但是,如果是这样,您应该考虑是否XYZ真的应该是一个内部类,也许它应该属于自己有时,创建一个静态内部类是有意义的(例如,如果您需要一个小型类来实现您的外部类正在使用的接口,而在其他任何地方都无济于事),但是大约一半的时间它应该是外部类。


2
外部类也可以访问静态内部类的私有成员,因此与static无关。您说“让各类能够看到彼此的内部运作没有任何意义”,但事实并非如此-如果仅让内部类看到外部类别的内部运作有意义,而对副类却没有看到,那该怎么办?反之亦然?
或Mapper 2014年

3

Thilo 为您的第一个问题“这怎么可能?” 添加了一个很好的答案。我想详细说明第二个问题:为什么允许这种行为?

首先,让我们非常清楚地知道,此行为并不限于内部类,内部类从定义上说是非静态嵌套类型。所有嵌套类型都允许使用此行为,包括嵌套枚举和接口,这些枚举和接口必须是静态的并且不能具有封闭实例。基本上,该模型是以下语句的简化:嵌套代码具有对封闭代码的完全访问权限,反之亦然。

那么,为什么呢?我认为一个例子可以更好地说明这一点。

想想你的身体和大脑。如果将海洛因注射入手臂,大脑会变高。如果您认为大脑的杏仁核区域对您的人身安全构成威胁,例如说一个黄蜂,他会让您的身体向相反的方向转而奔向山坡,而您却没有“三思”。

因此,大脑是人体的内在部分-奇怪的是,大脑也是人体的固有部分。在这种密切相关的实体之间使用访问控制将丧失其关系声明。如果确实需要访问控制,则需要将这些类进一步分成真正不同的单元。在此之前,它们是同一单位。进一步研究的一个驱动示例是查看Iterator通常如何实现Java 。

从封装代码到嵌套代码的无限制访问在很大程度上使它无济于事,无法向嵌套类型的字段和方法添加访问修饰符。这样做会增加混乱,并可能为Java编程语言的新手提供一种错误的安全感。


1
这应该是公认的答案。如此清晰和彻底。相反,被接受的答案甚至无法解决问题。
临时用户名

IMO仍然没有回答为什么我们不能轻易地将私有字段添加到内部类而外部类无法直接访问的问题。除非我错了,否则这会破坏内部类的主要情况之一-创建短暂的“类结构”,不可变类型。FWIW C#高兴地支持这一点:repl.it/repls/VengefulCheeryInverse
Josh M.

-1

内部类被视为外部类的属性。因此,无论内部类实例变量是否为私有,外部类都可以像访问其其他私有属性(变量)一样毫无问题地进行访问。

class Outer{

private int a;

class Inner{
private int b=0;
}

void outMethod(){
a = new Inner().b;
}
}

-2

因为您的main()方法在ABC类中,所以可以访问自己的内部类。


2
问题不是ABC类的成员是否可以访问嵌套在ABC该类中的类,而是为什么他们可以访问Java中嵌套在该类中的类的私有成员ABC
OR Mapper 2014年

我在问的同一天回答了这个问题。2年后有人编辑了问题,3年后有人提出了不赞成票。我敢肯定,编辑问题的人都会完全改变问题的措词。
aberrant80
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.