枚举类型和泛型
1.枚举类型
使用枚举类型,可以取代前面学习过的定义常量的方式,同时枚举类型还赋予程序在编译时进行检查的功能。本节就来详细个绍枚举类型。
1.1使用枚举类型创建常量
设置常量时,我们通常将常量放置在接口中,这样在程序中就可以直接使用。该常量不能被修改,因为在接口中定义常量时,该常量的修饰符为final与static。常规定义常量的代码如例17.1所示。
interface Constants{ // 将常量放置在接口中
public static final int ConstantsA = 1;
public static final int ConstantsB = 12;
}
public class ConstantsTest { // 将常量放置在枚举类型中
enum Constants2{
ConstantsA, ConstantsB
}
// 使用接口定义常量
public static void methodOne(int c){ // 定义一个方法,这里的参数为int型
switch (c){
case Constants.ConstantsA:
System.out.println("methodOne() ConstantsA");
break;
case Constants.ConstantsB:
System.out.println("methodOne() Constants B");
break;
}
}
public static void methodTwo(Constants2 c){
switch (c){
case ConstantsA:
System.out.println("methodTwo() ConstantsA");
break;
case ConstantsB:
System.out.println("methodTwo() Constants B");
break;
}
}
public static void main(String[] args){
ConstantsTest.methodOne(Constants.ConstantsA);
ConstantsTest.methodTwo(Constants2.ConstantsA);
ConstantsTest.methodTwo(Constants2.ConstantsB);
ConstantsTest.methodOne(3);
}
}
1.2深入了解枚举类型
1.操作枚举类型成员的方法
用户可以将一个枚举类型看作是一个类,它继承于java. lang. Enum类,当定义-一个枚举类型时,每-个枚举类型成员都可以看作是枚举类型的一个实例,这些枚举类型成员都默认被final、public、 static修饰 ,所以当使用枚举类型成员时直接使用枚举类型名称调用枚举类型成员即可。
由于枚举类型对象继承于java.lang. Enum类,所以该类中一些操作枚举类型的
方法都可以应用到枚举类型中。表17. 1中列举了枚举类型中的常用方法。
2.泛型
泛型实质上就是使程序员定义安全的类型。在没有出现泛型之前, Java也提供了对object的引用“任意化”操作,这种“任意化"操作就是对object引用进行向下转型及向上转型操作,但某些强制类型转换的错误也许不会被编译器捕捉,而在运行后出现异常,可见强制类型转换存在安全隐患,所以在此提供了泛型机制。
2.1 回顾向上转型和向下转型
public class UpDown {
private Object b;
public Object getB(){
return b;
}
public void setB(Object b){
this.b = b;
}
public static void main(String[] args){
UpDown t = new UpDown();
t.setB(new Boolean(true)); // 向上转型操作
System.out.println("getB is " + t.getB());
t.setB(new Float(12.3));
Float f = (Float) (t.getB()); // 向下转型操作
System.out.println(f);
}
}
在本实例中, Test类中定义了私有的成员变量b ,它的类型为0bject类型,同时为其定义了相应的setXXX( )与getXXX()方法。在类主方法中,将newBoolean(true )对象作为setB( )方法的参数,由于setB( )方法的参数类型为object ,这样就实现了向上转型操作。同时在调用getB( )方法时,将getB( )方法返回的bject对象以相应的类型返回,这个就是向下转型操作,问题通常就会出现在这里。因为向上转型是安全的,而如果进行向下转型操作时用错了类型,或者并没有执行该操作,就会出现异常,例如以下代码:
t.setB(new Float(9.9));
try {
Integer intToFloat = (Integer)(t.getB());
System.out.println(intToFloat);
}
catch (Exception e){
System.out.println("Type conversion error");
}
并不存在语法错误,所以可以被编译器接受,但在执行时会出现ClassCastException异常。这样看来,向下转型操作通常会出现问题,而泛型机制有效地解决了这一问题。
2.2定义泛型类
0bject类为最上层的父类,很多程序员为了使程序更为通用,设计程序时通常使传入的值与返回的值都以bject类型为主。当需要使用这些实例时,必须正确地将该实例转换为原来的类型,否则在运行时将会发生ClassCastException异常。
例17.10
public class OverClass<T> { // 定义泛型类
private T over; // 定义泛型成员变量
public T getOver(){ // 设置get()方法
return over;
}
public void setOver(T over){ // 设置set()方法
this.over = over;
}
public static void main(String[] args){
OverClass<Boolean> overOne = new OverClass<Boolean>(); // 实例化一个Boolean对象
OverClass<Float> overTwo = new OverClass<Float>(); // 实例化一个Float对象
overOne.setOver(true);
overTwo.setOver(new Float(1.65));
Boolean b = overOne.getOver();
Float f = overTwo.getOver();
System.out.println("boolean = "+ b + "; Float = " + f);
}
}
说明
在定义泛型类时,一般类型名称使用T来表达,而容器的元素使用E来表达,具体的设置读者可以参看JDK 5. 0以上版本的API。
2.3泛型的常规用法
2.3.1定义泛型类时声明多个类型,语法如下
MutiOverClass<T1, T2>
MutiOverClass:泛型类名
例如
MutiOverClass<Boolean,Float>=new MutiOverClass<Boolean,Float>();
2.3.2.定义泛型类时声明数组类型
例17.12
public class ArrayClass<T> {
private T[] array;
public void SetT(T[] array){
this.array = array;
}
public T[] getT(){
return array;
}
public static void main(String[] args){
ArrayClass<String> strA = new ArrayClass<String>();
String[] arrayA = {"memberA", "memberB", "memberC", "memberD"};
strA.SetT(arrayA);
for (int i = 0; i < strA.getT().length; i++){
System.out.println(strA.getT()[i]);
}
}
}
不能使用泛型来建立数组的实例
private T[] arrayA = new T[9];
2.3.3.集合类声明容器的元素
可以使用K和V两个字符代表容器中的键值和与键值相对应的具体。
public class MutilateOverClass<K, V>{
public Map<K, V> m = new HashMap<K, V>(); // 定义集合HashMap实例
// 设置put()方法,将对应的键值与键名存入集合对象中
public void put(K k, V v){
m.put(k, v);
}
public V get(K k){
return m.get(k); // 根据键名获取键值
}
public static void main(String[] args){
MutilateOverClass<Integer, String> mu = new MutilateOverClass<Integer, String>();
// 实例化泛型对象
for (int i = 0; i < 5; i++){
mu.put(i, " I'm a member of the collection");
// 根据集合的长度循环将键名与具体值放入集合中
}
for (int i = 0; i < mu.m.size(); i++){
System.out.println(mu.get(i)); // 调用get()方法获取集合中的值
}
}
}
public class AnyClass {
public static void main(String[] args){
// 定义ArrayList容器,设置容器内的值类型为integer
ArrayList<Integer> a = new ArrayList<Integer>();
a.add(1); // 为容器添加新值
for (int i = 0; i < a.size(); i++){ // 循环显示容器内的值
System.out.println("get value in ArrayList is"+a.get(i));
}
// 定义HashMap容器,设置容器的键名和键值类型分别为Integer与String型
Map<Integer, String> m = new HashMap<Integer, String>();
for (int i = 0; i < 5; i++){
m.put(i, "member " + i);
}
for (int i = 0; i < m.size(); i++){
// 根据键名获取键值
System.out.println("get Map container member is "+m.get(i));
}
// 定义Vector容器,使用容器中的内容为String型
Vector<String> v = new Vector<String>();
for (int i = 0; i < 5; i++){
v.addElement("member"+i); // 为vector容器中添加内容
}
for (int i = 0; i < v.size(); i++){
System.out.println("get Vector contain member is " + v.get(i));
}
}
}
2.4泛型的高级用法
泛型的高级用法包括限制泛型可用类型和使用类型通配符等。
2.4.1.限制泛型可用类型
语法如下
class 类名称<T extends anyclass>
其中anyclass是某个接口或类。
使用泛型限制后,泛型类的类型必须实现或继承了anyClass这个接口或类。无论anyClass是接口还是类,在进行泛型限制时都必须使用extends关键字。
public class LimitClass<T extends List> { // 限制泛型的类型
public static void main(String[] args){
// 可以实例化已经实现的List接口的类
LimitClass<ArrayList> l1 = new LimitClass<ArrayList>();
LimitClass<ArrayList> l2 = new LimitClass<ArrayList>();
// 这句是错误的,因为hashMap没有实现List()接口
// LimitClass<HashMap> l3 = new LimitClass<HashMap>();
}
}
在例17.15中,将泛型作了限制,设置泛型类型必须实现List接口。例如,ArrayList和L inkedList都实现了List接口,而HashMap没有实现List接口,所以在这里不能实例化HashMap类型的泛型对象。