私有类数组的长度无法访问

时间:2015-09-16 10:20:11

标签: java arrays

请考虑以下代码:

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;正在给出错误。

我实际上明白为什么会这样。这是因为子类B是私有的。但仍然为什么会这样。在A类中,属性arr是可访问的,但为什么不是它的长度。在jls或任何地方都有这方面的解释。

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

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

3 个答案:

答案 0 :(得分:4)

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

  1. Item 6.6.1确定辅助功能:

      

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

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

  2. Item 10.7数组成员:

      

    public final字段length,其中包含数组的组件数。长度可以是正数或零。

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

    现在,让我们进入一个更详细的例子来证明这意味着什么。我向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.plain是一个可见字段。 s将包含Hidden class B(99)。但如果你尝试:

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

    这是不允许的,因为toString()中的B是公开的,B本身是私有的,您无法访问其成员,无论是公共还是私有。

    请注意,我们无法访问i中的B,尽管它已公开。如果我们使用:

    plain.i
    

    然后由于i不是Object的成员,因此会出现编译错误。如果我们使用:

    a.plain.i
    

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

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

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

    这是合法的,尽管objArr在内部A.B[]。我们引用Object[]Object是公开的,Object[]也是如此。但是:

    int len = a.arr.length;
    

    完全按照a.plain.toString()的标准给出编译错误。虽然length本身是公开的,但您通过对A.B[]的引用来访问它。无法访问A.B[],因为A.B无法访问。因此,因为length是其成员,所以您无法访问它。根据上面的第一条规则,您根本无法访问任何您不可见的引用类型的成员。

    值得注意的是,以下 合法:

    Object firstItem = a.arr[0];
    

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

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

    <强>摘要

    • 如果您将其转换为某种公共超类型,可以暂停对私有类型的引用。
    • 可以在私有类型的数组中获取特定项目。索引数组不被视为&#34;访问成员&#34;,它只是一个引用的表达式,它为您提供对其成员类型的引用。您需要将其投射到公共场所才能使用。
    • 尝试访问具有私有类型的给定引用的成员是不正常的,即使该成员是公共的。这包括数组的length
    • 如果该超类型可用,则可以通过转换为超类型来访问该公共成员。 length中提供了Object [],因此您可以完成此操作。
    • 无法访问在可访问的超类型中不存在的私有类型的公共成员。

答案 1 :(得分:3)

这样做:

 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

  

在成员级别,您也可以使用public修饰符或no修饰符(package-private),就像使用顶级类一样,并且具有相同的含义。对于成员,还有两个额外的访问修饰符:private和protected。 private修饰符指定只能在自己的类中访问该成员。 protected修饰符指定只能在自己的包中访问该成员(与package-private一样),此外,还可以在另一个包中通过其类的子类访问该成员。

答案 2 :(得分:1)

知道问题是关于访问createBlockBlobFromText字段。但是,有趣的是我发现length可以通过增强型循环来确定,​​而不是通过更改访问权限或使用反射来确定:

length

关于数组的几个有趣的陈述是:

Arrays

  

在Java编程语言中,数组是对象(§4.3.1),是   动态创建,可以分配给Object类型的变量   (§4.3.2)。可以在数组上调用Object类的所有方法。

Array Types

  

数组的长度不属于其类型。