今天代码评审,看到有人将常量类写到了interface中,利用了interface的特性,定义变量省略了“public static final”的写法。看着确实美观,但好不好呢?以下是两种常量类具体写法:
1、常量类的写法
1.1 类定义常量,需要定义成final且定义一个private的构造方法,这样做是为了不让其他类继承,禁止实例化此类,调用时直接以"类.常量"的方式调用。
1.2 类定义常量,不能实例化也不能被继承,看起来完美无缺
2、interface写法
2.1 接口中定义的"变量",其实就是常量,接口中的"变量"默认都是 "public static final"类型,即为常量,因此接口可以省略"public static final"而直接写成 "type variable"。
2.2 接口定义常量,虽不能实例化,确可以被其他类实现;
对以上两种情况,我们编译代码 后通过jdk提供的反编译工具javap -c 分析
示例代码如下:
反编译如上可见,使用interface作为常量类,注意标号3的代码,由于引用了static final字段,编译器将interface Parent 中 name的内容编译进了 class child 中。而不是对 interface Parent 中的name的引用。如果这样,java的动态优势就消失了!这样只能重新编译整个项目,才能使常量类生效(ps:java是动态语言我们在java工程中有时部分内容改变,不用重新编译整个项目,而只需编译改变的部分重新发布就可以)
使用class作为常量类反编译结果跟interface一样。如果常量类属性定义为私有,通过get方法获取属性内容,就可实现java的动态功能,因为编译器将parent中name的引用编译进了Child中,而非真实的值.
个人总结:
1、不要使用"常量接口模式",此模式会导致类中的常量混乱,增加维护难度。注意!是常量接口模式时不要使用,而对于普通的设计是可以使用接口去实现的。
2、不要使用静态导入,import static *****,因为import static会导致可维护性下降,维护的人看代码时,不是那么清楚或者不那么迅速的知道这个常量位于哪个文件中。建议使用常量的地方直接 "接口.常量名" 的方式使用。
3、还有用这种方式定义常量: public abstract class Constants {},很容易看出这种方式定义常量的目的:禁止实例化。但是"abstract class"的目的是为了让其他类继承,因此语意别扭,同样具有interface定义常量的确定,即可以被继承, 其实解决上述问题更优美的方式是用final修饰类并给类定义一个private构造方法。
4、对于用是用interface定义常量还是使用class定义常量,看个人喜好. 个人觉得interface定义常量更为优美:代码更简洁, 生成的class文件更小,JVM不要考虑类的附加特性,如方法重载等,因而更为高效。这是一种反模式的用法,很多人不喜欢这种用法,如果我们知道它的优缺点,延长避短,也是无可厚非的,还有一点是不要把这种用法用成"常量接口模式",个人觉得"常量接口模式"确实是一种对interface的"pool use"。