为什么Java中的嵌套类在以下方面与C#中的嵌套类不同?

4
我发现有几篇文章与此类似,但没有找到一个能够深刻解释这个问题的答案。我已经对一个类进行了嵌套,即类“inside”存在于类“outside”中,并尝试实例化内部类,在这种情况下,遇到了以下情况:
关于C#:
    class outside
    {
        public class inside
        {
            public void print()
            {
                System.Console.WriteLine("I am inside");
            }
        }
    }
    class Program
    {
        public static void Main(string[] args)
        {
            /* here i am trying to instantiate the inner class 'inside' */
            outside.inside obj = new outside.inside();
            obj.print();
            Console.ReadKey();
        }
    }

输出:

我在里面。 所以,上面的工作很好... 但是,在Java中:

class outside
{
    public class inside
    {
        public void print()
        {
            System.out.println("I am inside");
        }
    }
}
class Demo
{
    public static void main(String[] args)
    {
        /* here i am trying to instantiate the class 'inside' */
        outside.inside obj=new outside.inside();
        obj.print();
    }
} 

输出:

Demo.java:16: 错误: 需要包含outside.inside的封闭实例...

这是Java中的情况。这个错误是什么?

这是否意味着外部类'outside'不能使用点运算符访问内部类'inside',因为它不是静态的?如果是这样,那么为什么在c#中同样的操作不会生成编译错误?


“static class”在这两种语言中有着截然不同的含义,因此非静态“class”的含义也不同。这在内部类中会发挥作用。您可能需要查阅每个上下文中的“static class”以帮助理解其含义。 - Tim S.
可能是重复的问题:需要包含<my reference>的封闭实例 - Joshua Taylor
4个回答

10

问题在于你在Java中声明类的方式,内部类对外部类的一个实例有隐式引用。因此会出现错误消息:"error: an enclosing instance that contains outside.inside is required"。

这意味着你需要:

Outside out = new Outside();
Outside.Inside in = out.new Inside();

通常情况下,在Java中,如果没有外部类的实例,内部类的实例是没有意义的。请注意,内部类的实例将可以访问外部类实例的所有变量,即使是私有变量。但是,这样的类通常是private的。

为了消除这种情况,您必须将内部类声明为static。然后您就可以执行以下操作:

Outside.Inside in = new Outside.Inside();

编辑:关于Java中static的含义,完整解释如下:static修饰的变量、类和方法可在类级别和实例级别访问。例如,在非静态方法中可以访问静态变量(这也意味着您可以从类的实例调用静态方法!);但是静态方法无法访问非静态变量或调用"仅实例"的方法等。

另外,“顶层”类不能是static,因为这没有意义。


1
通常情况下,在Java中,内部类应该是private(或public如果需要)static class MyInnerClass,除非你明确需要对包含类的引用。即使这样做可以通过在创建时传入对象引用来实现。在我看来,这应该是默认行为,因为它更清晰,但无论如何,仍然有解决方案。 - Java Drinker
1
好的,那就是我说的...还要注意我关于实例变量的说法。如果内部类是静态的,你就无法访问它们(当然,静态变量除外,但它们与类有链接,而不是实例)。 - fge
@fge 是的...那么如果是C#呢?内部架构是如何设计来处理这样的类的? - Srinivas Cheruku
@SrinivasCheruku 你所说的“内部架构”是什么意思?更一般地说,你应该知道在Java中static表示变量或类可以在类级别访问。C#的嵌套类由于这个原因等同于Java的嵌套静态类。当Java中没有static时,必须存在一个封闭实例才能访问方法/变量/类等。 - fge
1
更正:Java 中的 static 变量可以在类级别和实例级别访问。例如,您可以从非静态方法访问静态变量;但是静态方法无法访问非静态变量。 - fge

2

Java中嵌套类的语法与C#略有不同。以下是两者之间的更好比较。通过查看以下两个Java代码段的C#翻译,您可以更好地了解Java在幕后做了什么。有关另一个参考,请查看此链接

Java中的静态嵌套类(static nested class)的工作方式类似于C#中的嵌套类。

Java:

class Outside {
    public static class Inside {
        public void print() {
            System.out.println("I am inside");
        }
    }
}

class Demo {
    public static void main(String[] args) {
        Outside.Inside obj = new Outside.Inside();
        obj.print();
    }
}

C#:

class Outside
{
    public class Inside
    {
        public void Print()
        {
            System.Console.WriteLine("I am inside");
        }
    }
}

class Program
{
    public static void Main(string[] args)
    {
        Outside.Inside obj = new Outside.Inside();
        obj.Print();
    }
}

Java中的内部类(非静态嵌套类)在C#中没有直接的语法翻译,但是我们可以编写明确的等效代码。

Java:

class Outside {
    public class Inside {
        public void print() {
            System.out.println("I am inside");
        }
    }
}

class Demo {
    public static void main(String[] args) {
        Outside out = new Outside();
        Outside.Inside obj = out.new Inside();
        obj.print();
    }
}

C#:

class Outside
{
    public class Inside
    {
        private Outside _outer;

        public Inside(Outside outer)
        {
            if (outer == null)
                throw new ArgumentNullException("outer");

            _outer = outer;
        }

        public void Print()
        {
            System.Console.WriteLine("I am inside");
        }
    }
}

class Program
{
    public static void Main(string[] args)
    {
        Outside outside = new Outside();
        Outside.Inside obj = new Outside.Inside(outside);
        obj.Print();
    }
}

哦,天啊,我真希望一年前在开始这个安卓项目时就有这个C#翻译。有太多涉及内部类访问外部类私有实例成员的安卓本地Java代码片段,而我的翻译方法是在内部类中声明与外部类成员(我需要的那些)相匹配的公共成员,然后将值复制过去。这种方法太可怕了,尤其是要确保内部成员始终与外部成员保持同步,特别是在异步事件驱动的环境中!非常感谢您提供这个宝贵的资源。 - samus

1
在Java中,要实例化一个内部类,必须先实例化外部类。然后,使用以下语法在外部对象中创建内部对象:
OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();

为什么C#不同呢? 因为它是另一种语言。


@engima 是的.. 但归根结底,一切都以面向对象编程的概念为基础,这必须是普遍适用的.. - Srinivas Cheruku

0
在C#中,嵌套类(包括其静态和非静态方法)可以访问外部类类型的对象的所有静态和非静态成员和方法,包括外部类本身,无论保护级别如何。只有在从嵌套类外部或外部类外部访问时才会涉及到保护级别。
无论保护级别如何,您都不能在嵌套类类型的对象上访问外部类的非静态成员和方法(即使在外部类、嵌套类或其他类中访问也不可能)。您必须使用外部类对象。同样,您不能通过嵌套类名称访问外部类的静态成员和方法,您必须使用外部类名称。
外部类无法访问嵌套类的私有成员(静态或非静态)(通过嵌套类类型的对象或通过嵌套类本身),但它可以实例化该类,因为嵌套类本身的类类型始终对外部类可访问。这意味着,如果嵌套类是私有的,但静态成员不是私有的,则它将能够访问该成员,但如果成员是私有的,则它将无法访问该成员。在这种情况下,从外部类访问就像从程序集中的另一个类访问一样。

无论保护级别如何,您都无法在外部类类型的对象上访问嵌套类的非静态成员和方法,必须使用内部类对象。同样地,您无法通过外部类名称访问嵌套类的静态成员和方法,必须使用内部类名称。

在C#中,外部类和嵌套类对象始终作为单独的对象实例化,但您可以在嵌套类中放置对外部类的引用,在外部类中放置对嵌套类的引用。

在Java中,如果嵌套类不是静态的,则称之为内部类。内部和外部类需要分别实例化,但你需要使用外部对象才能实例化内部对象,这意味着内部对象始终与外部对象相关联。这意味着你需要首先实例化一个外部对象。然而,内部对象不是外部对象的成员,外部对象也不是内部对象的成员,因此你必须显式地将其作为一个成员以便于在类外访问它所配对的对象。在内部类中,你可以使用特殊版本的this ,使用outerclass.this.method 访问所有外部对象成员和方法 - 因为你需要this来做到这一点,所以你不能在内部类外部访问它。在外部类中,你无法使用innerclass.this.method2 访问与外部对象绑定的内部对象,因为特殊的this 仅用于获取this 对象的外部对象。这是因为一个外部对象可以有多个内部对象,所以这样做会产生歧义并被禁止。

Java和C#之间的另一个区别是,在Java中,您可以通过内部类类型的对象(如第3段所述)从外部类中访问私有内部成员。

Java中静态嵌套类与其他语言(如C++和C#)中的静态嵌套类不同,Java中没有相应的类。在Java中,静态嵌套类是C#中的常规嵌套类-它只是删除了需要使用外部类对象实例化嵌套类的要求,而是可以单独实例化外部和嵌套类的对象,因此特殊的this也不起作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接