Java函数式系列-闭包和内部类

Java中有关内部类的种类可分为4种,分别是

  • 成员内部类
  • 局部内部类
  • 匿名内部类
  • 静态内部类

一一来看吧。

1.先是成员内部类。成员内部类是我们见得最多的内部类,他就是在一个类的内部再声明一个类,类似于类的成员。在成员内部类中可以无条件访问外部类所有成员和属性,不管你是private还是static声明的。具体怎么访问直接看代码吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main {
public int test = 0;
private int pTest = -1;

public class Inner {
private int a = 1;
public int b = 1;

Inner() {
System.out.println(test);
System.out.println(pTest);
}
}

public static void main(String[] args) {
Main main = new Main();
main.new Inner();
}
}

输出:

0
-1

可以看到,我们要实例化内部类,必须先实例化外部类,然后通过外部类的引用来实例化内部类。这里注意,成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:

外部类.this.成员变量

外部类.this.成员方法

接着说局部内部类。

2.局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内

1
2
3
4
5
6
7
8
9
10
11
public class Main {
public int test = 0;
private int pTest = -1;

public static void main(String[] args) {
class Inner {
private int a = 1;
public int b = 1;
}
}
}

局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的

3.匿名内部类,顾名思义,就是个没有名字的内部类,怎么实现呢?

我可以先定义一个接口

1
2
3
interface test {
void show();
}

然后就可以在main方法里实现一个匿名内部类

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {

public static void main(String[] args) {
new test() {

@Override
public void show() {
System.out.println("我是匿名内部类");
}
}.show();
}
}

输出:

我是匿名内部类

很奇怪,接口怎么能被new?其实这是个编译器提供的语法糖,在编译期间隐式的创建了一个匿名内部类,这个类implements了test接口,并且由我们重写了接口的show()方法,这里可以使用eclipse来看一下生成的class文件就知道了

很明显的多出了一个Main$1的类,这就是编译器给我们隐式创建的匿名类。

4.静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。

所以我们可以直接通过外部类类名来实例化一个内部类,不想刚开始必须先实例化外部类才行

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {
public int test = 0;
private int pTest = -1;

static class Inner {
private int a = 1;
public int b = 1;
}

public static void main(String[] args) {
System.out.println(new Main.Inner().a);
}
}

输出:

1

最后来提一下闭包(Closure),简单来说,闭包是能够将一个方法作为一个变量去存储,并且这个方法有能力去访问所在类的自由变量。

有点绕人,换句话说,传统观念下我们知道函数里的变量都为局部变量,只在自身作用域内存在,不会被其他的函数读取到,那么闭包就是一种有能力访问其他函数中变量的函数,闭包是将函数内部和函数外部连接起来的桥梁。

所以按照我自己的理解,我更加倾向于将闭包理解成一种环境,我叫他闭包环境,在此环境下,内部和外部是被相互打通的。

那么闭包和上面所说的内部类的联系是什么呢?

我认为闭包是个大概念,很多领域都有闭包这个术语,包括离散数学(离散里的闭包和编程里说的闭包无直接联系)等等。所以内部类是实现闭包的一种手段而已,两者不可混为一谈。

在Java中,闭包本质上就是把外部类的一个变量拷贝给了内部类里面的另一个变量,实现为浅拷贝,所以为了保证闭包内外环境中变量引用所指向的对象保持同步,编译器会强制我们给需要使用的变量加上final修饰符,防止变量在内部类的浅拷贝之后造成的不同步情况。

这也是说Java的闭包是残缺的闭包,也有人说Java中没有真正的闭包的原因了。

文章作者: Shawn Qin
文章链接: https://qinshuang1998.github.io/2019/02/06/function-coding-02/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Shawn's Blog