C# Java 内部类之间的比较

C# Java 内部类之间的比较

  一、引言

 严格来说,C#并没有“内部类”这个概念,它应该叫做“嵌套类” (Nested Class)。下面先看一段Cclass Outer
{
    void Print()
    {
        Console.WriteLine("Hello World!");
    }
    class Inner
    {
        void Show()
        {
            Print();
        }
    }
}


  遗憾的是,这一段代码不能通过编译。编译器会产生如下错误:无法通过嵌套类型“Outer.Inner”来访问外部类型“Outer”的非静态成员。

  将这一段代码复制到Java中:

class Outer
{
    void Print()
    {
        System.out.println("Hello World!");
    }
    class Inner
    {
        void Show()
        {
            Print();
        }
    }
}
  发现不报错。为什么Java不报错而C#报错呢?两种语言对待这种“类中类”,其处理方式有什么差别?这是我接下来想探讨的问题。

  二、C#中的嵌套类

  从上面的例子我们至少可以看出一点:在C#中,类里面定义一个新类,和外部的类是没有任何关系的。虽然Inner类定义在Outer中,但是不能访问Outer中的成员,此时,Outer更加像一个命名空间。我们可以用诸如Outer.Inner i = new Outer.Inner();之类的语句来实例化一个Inner对象。

  甚至可以在C#中加入一个嵌套静态类:

class Outer
{
    static class Inner
    {
        public static int i = 0;
        public static void Increase()
        {
            i++;
            Console.WriteLine(i);
        }
    }
    static void Main(string[] args)
    {
        Outer.Inner.Increase();
    }
}

  Outer在Main方法中的作用,和命名空间没有多大差别。

  因为C#中的嵌套类逻辑如此简单,所以一切都是“理所当然”的,书中不会提及太多。顺便说一下,随着var关键字的引入,C#是支持匿名类的,例如语句var A = new { a = 3 } ;创建了一个匿名类的对象,此对象里有一个Int32成员a,被赋予初值3。

  以上的特性,在Java程序中就完全不一样了。

  三、Java中的内部类(Enclosing Type)

  在第一点的Java代码段中,我们了解到,内部类Inner可以调用外部类的方法,这种行为我们叫做“闭包”(Enclosure),也就是。创建内部类的对象的我是这样理解的:在创建了内部类后,编译器同时还创建了一个“隐藏的引用”(姑且叫它为Outer.this)。在最开始的例子中,我们调用Inner的Print方法时,其实调用的是Outer.this.Print。因为这样的特性,Java中的内部类理解起来会比C#要麻烦。

  我们在C#中新建一个嵌套类的对象,可以用以下语句:

Outer.Inner i = new Outer.Inner();

  但是,我们不能用这种方式在Java中实例化一个内部类。其原因是,既然内部类中包含了一个外部类的引用,那么我们在创建内部类实例的时候,就必须要指明外部类的实例的。我们用以下特殊的语法:

Outer o = new Outer();
Outer.Inner i = o.new Inner();

  首先,我们定义了一个外部类的实例o,然后我们必须要用.new方式来创建内部类的实例:o.new Inner(),并且需要注意的是,不能写成o.new Outer.Inner(),否则编译器会报错。

  如果我们需要在内部类中返回外部类的实例,要用[外部类名].this。如上面的两行代码,如果我需要在Inner类中返回外部类实例o,需要写成:

return Outer.this;

  你可以在类中嵌套若干层内部类,可以在任意一层调用外部类的成员。若内部类的层次之上存在同名成员,它会返回里内部类最近的那一层的成员。Java对内部类的限制很少,甚至可以在方法体中定义一个内部类。

  四、Java中的匿名类

  Java中的匿名类和C#中的匿名类不同。Java中的匿名类要以某个类为基类进行扩展。例如以下Java代码段:

class Main
{
	class A {}
	public A test(){
		return new A(){
			{ num = 5; }
			void run(){}
			int num;
			int num2 = 3;
		};
	}
	public static void main(String[] args) {}
}

  这个代码风格在C#中很奇怪,下面简单讲解以下。return new A() { 表示创建一个继承于A的一个匿名类(也就是我们不知道这个类叫做什么名字),类的成员声明是A()后的花括号{}所包围的部分。 { num = 5; } 表示的是这个匿名类的构造方法 (因为匿名类没有名字,所以只需要用花括号包围起来)。test方法最终返回了一个向上转型为A类型的匿名类,这个类只能在test方法中存在,不得超出其域。
  如果想通过传参的方式给匿名类中的成员赋值,参数必须final的。例如下面的代码:

class Main
{
	class A {}
	public A test(final int i){
		return new A(){
			int num = i;
		};
	}
	public static void main(String[] args) {}
}

  如果把final去掉,将会得到一个编译错误。

  上段代码等效为:

class Main
{
	class A {}
	public A test(final int i){
		class TempClass extends A {
			int num = i;
		}
		return new TempClass();
	}
	public static void main(String[] args) {}
}

  五、Java中的嵌套类

  有了以上基础,理解Java中的嵌套类(nested class)就很容易了。Java的嵌套类就是C#的嵌套类。Java中定义嵌套类的方法是将内部类声明为static的。无论是C#还是Java,一旦一个类是一个嵌套类,它将无法访问外部类的非静态成员。作为一个Java的内部类,不得包含static成员和方法,但是嵌套类是可以的。

 
 
 

 

你可能感兴趣的:(java,内部类,C#,嵌套类)