最近看了js的闭包,仔细想了想,感觉java也可以模拟js的闭包特性实现属性封装,不过看似没什么卵用。
下面,首先定义一个Person类,该类中并没有任何属性,我们将属性 pname和page放到一个静态方法中,作为局部变量,对!你没有看错,局部变量!
然后用lambda表达式来set或get 这些局部变量。
最后将这些lambda表达式(匿名内部类)放入一个map中做为返回值返回。
具体代码如下:
package com.fly.domain;
import java.util.HashMap;
import java.util.Map;
public class Person {
//静态方法获取Person的操作接口
public static Map<String, PersonInterface> getPerson() {
//定义局部变量 pname和page
String[] pname = new String[1];
Integer[] page = new Integer[1];
//定义操作局部变量的接口
PersonInterface getName = (v) -> {
return pname[0];
};
PersonInterface getAge = (v) -> {
return page[0];
};
PersonInterface setName = (v) -> {
pname[0] = (String) v;
return null;
};
PersonInterface setAge = (v) -> {
page[0] = (Integer) v;
return null;
};
//将操作接口放入map
Map<String, PersonInterface> person = new HashMap<>();
person.put("setName", setName);
person.put("setAge", setAge);
person.put("getName", getName);
person.put("getAge", getAge);
//返回接口map
return person;
}
//定义一个函数式接口,用于lambda表达式
public static interface PersonInterface {
Object apply(Object value);
}
}
下面测试下这个四不像的类吧:
public static void main(String[] args) {
//做俩Person出来(其实是两个Map,内部封装了操作pname和page的接口)
Map<String, Person.PersonInterface> p1 = Person.getPerson();
Map<String, Person.PersonInterface> p2 = Person.getPerson();
//给属性赋值
p1.get("setName").apply("老王");
p1.get("setAge").apply(66);
p2.get("setName").apply("老李");
p2.get("setAge").apply(55);
//输出
Object name1 = p1.get("getName").apply(null);
Object age1 = p1.get("getAge").apply(null);
System.out.println("p1.name = " + name1 + ", and p1.age = " + age1);
Object name2 = p2.get("getName").apply(null);
Object age2 = p2.get("getAge").apply(null);
System.out.println("p2.name = " + name2 + ", and p2.age = " + age2);
}
输出结果:
p1.name = 老王, and p1.age = 66
p2.name = 老李, and p2.age = 55
目前看来,这种方法确实可以封装属性,(值得注意的是:封装的属性我用了数组来实现,因为lambda表达式内部无法修改外部变量,我只能采用间接的方法来实现)但是不知道为什么匿名内部类会把一个方法内部的局部变量给带到外边来,关键是:这是否会影响垃圾回收。
不过这种方法的确把属性封装的死死的,似乎暴力反射也取不出来。底层原理还在研究中。。。
更新,关于原理初步探讨
废话不多说,上图:
可以看到,所有的Lambda表达式都被编译成了一个
Person$lambda
的内部类(其中Person是该lambda表达式所在的类),其内部封装了一个属性:**arg$1**
,看来,这个属性就是lambda表达式携带的局部变量!!!
从图中可以看出,“老王”,“66”这些属性都被封装到了lambda表达式内部,作为成员属性。