[自动装箱/自动拆箱]
所谓装箱,就是把值类型用它们相对应的引用类型包起来,使它们可以具有对象的特 质,如我们可以把int型包装成Integer类的对象,或者把double包装成Double,等等。 所谓拆箱,就是跟装箱的方向相反,将Integer及Double这样的引用类型的对象重新简化 为值类型的数据。
在J2SE(TM) 5.0发布之前,我们只能手工的处理装箱和拆箱。也许你会问,为什么需要 装箱和拆箱?比方说当我们试图将一个值类型的数据添加到一个Collection中时,就需 要先把它装箱,因为Collection的add()方法只接受对象;而当我们需要在稍后将这条数 据取出来,而又希望使用它对应的值类型进行操作时,我们又需要将它拆箱成值类型的 版本。现在,编译器可以帮我们自动地完成这些必要的步骤。下面的代码我提供两个版 本的装箱和拆箱,一个版本使用手工的方式,另一个版本则把这些显而易见的代码交给 编译器去完成:
public static void manualBoxingUnboxing(int i) {
ArrayList aList = new ArrayList();
aList.add(0, new Integer(i));
int a = aList.get(0).intValue();
System.out.println("The value of i is " + a);
}
public static void autoBoxingUnboxing(int i) {
ArrayList aList = new ArrayList();
aList.add(0, i);
int a = aList.get(0);
System.out.println("The value of i is " + a);
}
看到了吧,在J2SE(TM) 5.0中,我们不再需要显式的去将一个值类型的数据转换成相应 的对象,从而把它作为对象传给其他方法,也不必手工的将那个代表一个数值的对象拆 箱为相应的值类型数据,只要你提供的信息足够让编译器确信这些装箱/拆箱后的类型在 使用时是合法的:比方讲,如果在上面的代码中,如果我们使用的不是 ArrayList而是ArrayList或者其他不兼容的版本如 ArrayList,会有编译错误。
当然,你需要足够重视的是:一方面,对于值类型和引用类型,在资源的占用上有相当 大的区别;另一方面,装箱和拆箱会带来额外的开销。在使用这一方便特性的同时,请 不要简单的忘记了背后隐藏的这些也许会影响性能的因素。
[类型安全的枚举]
在介绍J2SE(TM) 5.0中引入的类型安全枚举的用法之前,我想先简单介绍一下这一话题 的背景。
我们知道,在C中,我们可以定义枚举类型来使用别名代替一个集合中的不同元素,通常 是用于描述那些可以归为一类,而又有限数量的类别或者概念,如月份、颜色、扑克 牌、太阳系的行星、五大洲、四大洋、季节、学科、四则运算符,等等。它们通常看上 去是这个样子:
typedef enum {SPRING, SUMMER, AUTUMN, WINTER} season;
实质上,这些别名被处理成int常量,比如0代表SPRING,1代表SUMMER,以此类推。因为 这些别名最终就是int,于是你可以对它们进行四则运算,这就造成了语意上的不明确。
Java一开始并没有考虑引入枚举的概念,也许是出于保持Java语言简洁的考虑,但是使 用Java的广大开发者对于枚举的需求并没有因为Java本身没有提供而消失,于是出现了 一些常见的适用于Java的枚举设计模式,如int enum和typesafe enum,还有不少开源的 枚举API和不开源的内部实现。
我大致说一下int enum模式和typesafe enum模式。所谓int enum模式就是模仿C中对 enum的实现,如:
public class Season {
public static final int SPRING = 0;
public static final int SUMMER = 1;
public static final int AUTUMN = 2;
public static final int WINTER = 3;
}
这种模式跟C中的枚举没有太多本质上的区别,C枚举的局限它基本上也有。而typesafe enum模式则要显得健壮得多:
public class Season {
private final String name;
private Season(String name) {
this.name = name;
}
public String toString() {
return name;
}
public static final Season SPRING = new Season("spring");
public static final Season SUMMER = new Season("summer");
public static final Season AUTUMN = new Season("autumn");
public static final Season WINTER = new Season("winter");
}
后一种实现首先通过私有的构造方法阻止了对该类的继承和显式实例化,因而我们只可 能取得定义好的四种Season类别,并且提供了方便的toString()方法获取有意义的说 明,而且由于这是一个完全意义上的类,所以我们可以很方便的加入自己的方法和逻辑 来自定义我们的枚举类。
最终,Java决定拥抱枚举,在J2SE(TM) 5.0中,我们看到了这一变化,它所采用的设计 思路基本上就是上面提到的typesafe enum模式。它的语法很简单,用一个实际的例子来 说,要定义一个枚举,我们可以这样写:
public enum Language {CHINESE, ENGLISH, FRENCH, HUNGARIAN}
接下来我们就可以通过Language.ENGLISH来使用了。呃…这个例子是不是有点太小儿科 了,我们来看一个复杂点的例子。使用Java的类型安全枚举,我们可以为所有枚举元素 定义公用的接口,然后具体到每个元素本身,可以针对这些接口实现一些特定的行为。 这对于那些可以归为一类,又希望能通过统一的接口访问的不同操作,将会相当方便。 通常,为了实现类似的功能,我们需要自己来维护一套继承关系或者类似的枚举模式。 借用Java官网上的例子:
public enum Operation {
PLUS { double eval(double x, double y) { return x + y; } },
MINUS { double eval(double x, double y) { return x - y; } },
TIMES { double eval(double x, double y) { return x * y; } },
DIVIDE { double eval(double x, double y) { return x / y; } };
// Do arithmetic op represented by this constant
abstract double eval(double x, double y);
}
在这个枚举中,我们定义了四个元素,分别对应加减乘除四则运算,对于每一种运算, 我们都可以调用eval()方法,而具体的方法实现各异。我们可以通过下面的代码来试验 上面这个枚举类:
public static void main(String args[]) {
double x = Double.parseDouble(args[0]);
double y = Double.parseDouble(args[1]);
for (Operation op : Operation.values()) {
System.out.println(x + " " + op + " " + y + " = " + op.eval(x, y));
}
}
怎么样,使用枚举,我们是不是能够很方便的实现一些有趣的功能?其实说穿了,Java 的类型安全枚举就是包含了有限数量的已生成好的自身实例的一种类,这些现成的实例 可以通过类的静态字段来获取。
<<上一页
1
2
3
4
下一页>>
|