私有类数组的长度不可访问

2022-09-03 15:38:11

请考虑以下代码:

class A {
    B[] arr = new B[10];

    private class B {}
}


class C {
    void fun(){
        A a = new A();
        Object arr = a.arr;
        Object len = a.arr.length;  // !! ERROR
    }
}

正如我在代码中写的。 给出错误。a.arr.length;

我实际上理解为什么会发生这种情况。这是因为子类是私有的。但仍然是为什么它会发生。在A类中,属性是可访问的,但为什么不是长度。在jls或任何地方对此有任何解释吗?Barr

我只想对这种行为有一个明确的解释。我知道私人的东西不能在它的类之外访问。但公共阵列可能是。无论它是什么类型。如果任何东西都可以在外面访问,那么它的公共属性也应该被访问。但在这里它没有发生。

编辑:我发现在C#中甚至不可能创建私有类数组。在java中,如果我们无法访问任何东西,甚至不知道私有类数组的长度,那么创建私有类数组有什么用。


答案 1

其原因是JLS中两个语句的组合:

  1. 项目6.6.1确定可访问性:

    当且仅当数组类型的元素类型可访问时,数组类型才可访问。

    这意味着如果 是私有的,也被视为私有的。TT[]

  2. 项目10.7阵列成员:

    字段 ,它包含数组的分量数。长度可以是正数或零。public finallength

请记住,可访问性是在编译时根据您拥有的引用类型确定的,而不是基于实际对象的类型!

现在,让我们进入一个更详细的示例来演示这意味着什么。我向 中添加了一个和一个构造函数。toString()B

class A {
    B[] arr = { new B(1), new B(2), new B(3), new B(4) };
    B plain = new B(99);

    private class B  {
        public int i;
        B(int i) {
            this.i = i;
        }
        @Override
        public String toString() {
            return "Hidden class B(" + i + ")";
        }

    }
}

现在,在类C中,我们使用:

A a = new A();
Object plain = a.plain;
String s = plain.toString();

这是合法的,因为它是一个可见的字段。 将包含 .但如果你尝试:a.plainsHidden class B(99)

String s = a.plain.toString(); // Compile error

这是不允许的,因为althogh是公共的,本身是私有的,你无法访问它的成员,无论是公共的还是私人的。toString()BB

请注意,尽管它是公开的,但我们仍然无法访问。如果我们使用:iB

plain.i

然后由于 不是 的成员,你会得到一个编译错误。如果我们使用:iObject

a.plain.i

然后,由于是私有的,因此您无法访问其成员,正如我们已经尝试过的那样。a.plain

所以现在我们来看看数组的问题。假设我们写:

Object[] objArr = a.arr;
int len = objArr.length;

这是合法的,尽管事实是内部的 。我们有一个引用,是公开的,也是。但:objArrA.B[]Object[]ObjectObject[]

int len = a.arr.length;

为您提供的编译错误与 我们对 .尽管它本身是公共的,但您正在通过对 的引用来访问它。 不可访问,因为 不可访问。因此,因为它是其成员,因此您无法访问它。根据上面的第一条规则,您根本无法访问对您不可见的引用类型的任何成员。a.plain.toString()lengthA.B[]A.B[]A.Blength

有趣的是,以下是合法的

Object firstItem = a.arr[0];

我们可以使用该表达式,因为它不被视为尝试访问数组的成员。数组的元素不被视为其中的成员。 只是解析为类型 的数组引用上的表达式。只要我们不尝试访问项目的成员,这样的表达式就没有问题。a.arr[0]a.arr[0]A.B

firstItem.toString() // Good
a.arr[0].toString()  // Bad

总结

  • 可以保留对私有类型的引用,前提是将其转换为某个公共超类型。
  • 可以在私有类型的数组中获取特定项。为数组编制索引不被视为“访问成员”,它只是引用上的一个表达式,它为您提供了对其成员类型的引用。您需要将其投射到公共内容才能使用。
  • 尝试访问具有对私有类型的给定引用的成员是不允许的,即使该成员是公共的。这包括数组的 。length
  • 可以通过强制转换为超类型来访问该公共成员(如果它在该超类型中可用)。 是可用的,所以你可以得到它通过它。lengthObject []
  • 无法访问在可访问超类型中不存在的私有类型的公共成员。

答案 2

请执行下列操作:

 class A {
    B[] arr = new B[10];

    public int getArrayLength()
    {
        return arr.length;
    }
    private class B {}
}


class C {
    void fun(){
        A a = new A();
        Object arr = a.arr;
        //Object isn't type safe
        //Object len = a.getArrayLength();
        int len = a.getArrayLength();
    }
}

根据JavaDocs

在成员级别,还可以使用公共修饰符或不修饰符(包专用),就像使用顶级类一样,并且具有相同的含义。对于成员,还有两个附加的访问修饰符:私有和受保护。私有修饰符指定只能在其自己的类中访问成员。受保护的修饰符指定只能在其自己的包中访问成员(与包专用一样),此外,还可以由另一个包中其类的子类访问。