群组常量
接口中的字段自动是 public 的,同时也自动是 static 和 final 的,所以接口就成为了一种很便捷的用来创建常量组的工具 (Java 中标识有 static final 时,使用大写字母的风格,并在一个标识符中用下划线来分割多个单词 ) 。
/** */ /** * Title: About constants in interfaces<br> * Description: Using interfaces to create groups of constants<br> * Company: Augmentum Int<br> * Copyright: 2008 (c) Thinking in Java<br> * @author Forest He * @version 1.0 */ package com.augmentum.foresthe; public interface Months ... { int JANUARY = 1 , FEBRUARY = 2 , MARCH = 3 , APRIL = 4 , MAY = 5 , JUNE = 6 , JULY = 7 , AUGUST = 8 , SEPTEMBER = 9 , OCTOBER = 10 , NOVEMBER = 11 , DECEMBER = 12 ; }
我们可以使用包外部的常量,其方式是先导入 com.augmentum.foresthe( 就像导入其他包一样 ) ,然后用诸如 Months.JANUARY 这样的表达式来引用它的值。当然所获得的只是一个 int 值,并没有额外的类型安全,但是这项被广泛应用的技术比起在程序中硬编码数字显然是一种提高 ( 硬编码数字的方式通常被称为“魔幻数字”,它会产生难以维护的代码 ) 。
如果想要额外的类型安全,那么可以像下面这样构建类:
/** */ /** * Title: About constants in interfaces<br> * Description: Using interfaces to create groups of constants<br> * Company: Augmentum Int<br> * Copyright: 2008 (c) Thinking in Java<br> * @author Forest He * @version 1.0 */ package com.augmentum.foresthe; public final class Month ... { private String name; private Month(String name) ... { this .name = name; } public String toString() ... { return name; } private static final Month JAN = new Month( " January " ), FEB = new Month( " February " ), MAR = new Month( " March " ), APR = new Month( " April " ), MAY = new Month( " May " ), JUN = new Month( " June " ), JUL = new Month( " July " ), AUG = new Month( " August " ), SEP = new Month( " September " ), OCT = new Month( " October " ), NOV = new Month( " November " ), DEC = new Month( " December " ); private static final Month[] month = ... { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC } ; public static final Month getMonth( int ord) ... { return month[ord - 1 ]; } public static void main(String[] args) ... { Month month = Month.APR; System.out.println(month); month = getMonth( 10 ); System.out.println(month); System.out.println(month == Month.OCT); System.out.println(month.equals(Month.OCT)); } }
Month 是一个具有 private 构造器的 final 类,所以无法继承它,也无法创建它的任何实例。所有的实例都是在该类内部由其自身创建的 final static 实例: JAN 、 FEB 、 MAR 等。这些对象会在 month 数组中用到,该数组允许你遍历一个由 Month 对象构成的数组。在 main() 中可以看到类型安全性: m 是一个 Month 对象,因此它仅仅可以被赋予一个 Month 值;而前面的例子 Months.java 仅提供了 int 值,因此用来表示月份的 int 变量实际上可以被赋予任何整数值,这就不是很安全了。
补充:在 java.util.Calendar 中也有一个表示月份的字段; Apache 的 Jakarta Commons 项目包含了用来创建枚举类型的工具。
初始化接口中的字段
在接口中定义的字段自动是 static 和 final 的。它们不能是“空 final ”,但是可以被非常量表达式初始化。
/** */ /** * Title: Initializing interface<br> * Description: Initializing interface fields with non-constant initializers<br> * Company: Augmentum Int<br> * Copyright: 2008 (c) Thinking in Java<br> * @author Forest He * @version 1.0 */ package com.augmentum.foresthe; import java.util.Random; public interface RandVals ... { Random rand = new Random(); int randomInt = rand.nextInt( 100 ); long randomLong = rand.nextLong(); float randomFloat = rand.nextFloat(); double randomDouble = rand.nextDouble(); } /**/ /* 既然字段是static的, 它们就可以在类第一次被加载时初始化, 这发生在任何字段首次被访问时. */ class TestRandVals ... { public static void main(String[] args) ... { System.out.println(RandVals.randomInt); System.out.println(RandVals.randomLong); System.out.println(RandVals.randomFloat); System.out.println(RandVals.randomDouble); } }
当然了,这些字段不是接口的一部分,只是被存储在该接口的静态存储区域内。
嵌套接口,可参见 InnerClass 。
和非嵌套接口一样,可以拥有 public 和“包访问”两种可视性;并且可以被实现为 public 、包访问和 private 的嵌套类 ( 实现一个 private 接口只是一种方式,它可以强制该接口中的方法定义不要添加任何类型信息,即不允许向上转型 ) 。