第十一章 枚举与泛型总结

        

目录

11.1 枚举

11.1.1  使用枚举类型设置常量

11.1.2  深入了解枚举类型

11.1.3  使用枚举类型的优势

11.2  泛型

11.2.1 回顾“向上转型”与“向下转型”

11.2.2 定义泛型类 

11.2.3 泛型的常规用法

11.2.4  泛型的高级用法

11.2.5  泛型总结

        JDK 1.5 中新增了枚举类型与泛型。枚举类型可以取代以往常量的定义方式,即将常量封装在类或接口中,此外,它还提供了安全检查功能。泛型的出现不仅可以让程序员少写某些代码,主要的作用是解决类型安全问题,它提供编译时的安全检查,不会因为将对象置于某个容器中而失去其类型。

11.1 枚举

        JDK 1.5中新增了枚举,枚举是一种数据类型,它是一系列具有名称的常量的集合。比如在数学中所学的集合:A=(1,2,3),当使用这个集合时,只能使用集合中的1、2、3这3个元素,不是这3个元素的值就无法使用。Java中的枚举是同样的道理,比如在程序中定义了一个性别枚举,里面只有两个值:男、女,那么在使用该枚举时,只能使用男和女这两个值,其他的任何值都是无法使用的。

11.1.1 使用枚举类型设置常量

        以往设置常量,通常将常量放置在接口中,这样在程序中就可以直接使用,并且该常量不能被修改,因为在接口中定义常量时,该常量的修饰符为final与static。

例如,在项目中创建Constants接口,在接口中定义常量的常规方式。

public interface Constants {
    public static final int Constants A=1;
    public static final int Constants_B=12;
}

        在JDK 1.5版本中新增枚举类型后就逐渐取代了这种常量定义方式,因为通过使用枚举类型,可以赋予程序在编译时进行检查的功能。使用枚举类型定义常量的语法如下:

public enum Constants {
    Constants_A;
    Constants_B;
    Constants C;
}

          其中,enum是定义枚举类型的关键字。当需要在程序中使用该常量时,可以使用Constants.Constants A来表示。 

例11.1  在项目中创建Constants接口,在该接口中定义两个整型变量,其修饰符都是static和final;之后定义名称为Constants2的枚举类,将Constants接口的常量放置在该枚举类中;最后,创建名称为Constants 的类文件。在该类中先通过doit(O和doit2()进行不同方式的调用,再通过主方法进行调用,体现枚举类型定义常量的方式。实例代码如图所示:     

                                                                                                                                                             运行结果如图所示:     

        在上述代码中,当用户调用doit方法时, 即使编译器不接受在接口中定义的常量参数,也不会报错:但调用doit2方法,任息传递参数,编译器就会报错,因为这个方法只接受校举类型的常最作为其参数。

说明:
        枚举类型可以在类的内部进行定义,也可以在类的外部定义。如果在类的内部定义,则类似于内部类形式,比如例11.1中,当编译该类时,除了CnstantsTest.class 外,还存在ConsantsTest$1.class 与ConstantsTest$Constants2.class文件。

11.1.2  深入了解枚举类型

1.操作枚举类型成员的方法

枚举类型较传统定义常量的方式,除了具有参数类型检测的优势之外,还具有其他方面的优势。用户可以将一个枚举类型看作是一 个类, 它继承于javalang Enum类,当定义- -个枚举类型时,每一个枚举类型成员都可以看作是枚举类型的一个实例, 这些枚举类型成员都默认被final、publicstatic修饰,所以当使用枚举类型成员时直接使用枚举类型名称调用枚举类型成员即可。

由于枚举类型对象继承于java.lang Enum类所以该类中一些操作 枚举类型的方法都可以应用到枚举类型中。表11.1中列举了枚举类型中的常用方法。

表11.1枚举类型的常用方法:

(1) values()
权举类型实例包含一个 values()方法,该方法可以将枚举类型成员以数组的形式返回。

例题11.2   在项目中创建 ShowEnum类,在该类中使用枚举类型中的values()方法获取枚举类型中的成员变量。实例代码如图所示:

运行结果如图所示: 

        在例11.2中,由于values()方法将枚举类型的成员以数组的形式返回,所以根据该数组的长所进行循环操作,然后将该数组中的值返回。

(2) valueOf()与 compareTo()
        枚举类型中静态方法valueOfz可以将普通字符串转换为枚举类型,而compareTo()方法用于较两个枚举类型成员定义时的顺序。调用comparTo()方法时,如果方法中参数在调用该方法的村对象位置之前,则返回正整数:如果两个互相比较的枚举成员的位置相同,则返回0;如果方法中参数在调用该方法的枚举对象位置之后,则返回负整数。

例11.3   在项目中创建 EnumMethodTest类,在该类中使用枚举类型中的valueOf()与comparerar方法.。实例代码如图所示:

运行结果如图所示: 

(3)ordinal()

枚举类型中的ordinal()方法用于获取某个枚举对象的位置索引值

例题11.4   在项目中创建EnumIndexTest类,在该类中使用枚举类型中的ordinal()方法用于获取某个枚举对象的位置索引。实例代码如图所示:

运行结果如图所示:

        本例11.4中,当循环中获取每个枚举对象时,调用ordinal()方法即可相应获取该枚举类型成员的索引位置。

2.枚举类型中的构造方法
        在枚举类型中,可以添加构造方法,但是规定这个构造方法必须为private 修饰符或者默认修饰符所修饰。枚举类型定义的构造方法语法如下: 

public enum Constants2{
Constants A("我是枚举成员A"),
Constants B("我是枚举成员B"),
Constants C("我是枚举成员C"),
Constants D(3) ;
String description;
int i;
private Constants2() {}
 
//定义默认构造方法
 
//定义带参数的构造方法,参数类型为字符串型
 
private Constants2 (string description) {
 
this. description=description;
}
private cosants2(int i){ //定义带参数的构造方法,参数类型为整型this. i=this. i+i;
}
}

        从枚举类型构造方法的语法中可以看出,无论是无参构造方法还是有参构造方法,修饰权限都为private定义一个有参构造方法后,需要对枚举类型成员相应地使用该构造方法,如Constants_A("我是枚举成员A")和Constats_D(3)语句,相应地使用了参数为String 型和参数为int 型的构造方法。然后可以在校华类型中定义两个成员变量,在构造方法中为这两个成员变量赋值,这样就可以在枚举类型中定义该成员变量的gxXX()方法了。

例题11.5   在项目中创建EnumConTest类在该类中定义枚举类型的构造方法。实例代码如图所示:

运行结果如图所示:


        在本实例中,调用getDescription()和getI()方法,返回在枚举类型定义的构造方法中设置的操作。这里将枚举类型中的构造方法设置为private修饰,以防止实例化一个枚举对象。
除了可以使用例11.5中所示的方法定义getDescription()方法获取枚举类型成员定义时的描述之外,还可以将这个getDescription()方法放置在接口中,使枚举类型实现该接口,然后使每个枚举类型实现接口中的方法。

例11.6   在项目中创建EnumInterface接口和枚举类型的AnyEnum类,在枚举类型AnyEnum类中实现带方法的接口,使每个枚举类型成员实现该接口中的方法。实例代码如图所示:

运行结果如图所示:

注意:
(1)  从上面代码中可以看出,枚举类型可以实现一个或者多个接口,但是它不能继承类。因为编译器会默认将枚举类型继承自 java.lang.Enum类,这一过程由编译器完成。
(2)枚举类型中的常量成员必须在其他成员之前定义,否则这个枚举类型不会产生对象。

11.1.3  使用枚举类型的优势

        枚举类型声明提供了一种用户友好的变量定义方法,枚举了某种数据类型所有可能出现的值。总结枚举类型,它具有以下特点:

(1)类型安全。
(2)  紧凑有效的数据定义。
(3)可以和程序其他部分完美交互。

(4)运行效率高。

11.2  泛型

        在JDK 1.5版本中提供了泛型概念,泛型实质上就是使程序员定义安全的类型。在没有出现泛型之前,Java也提供了对Object 的引用“任意化”操作,这种任意化操作就是对Object引用进行“向下转型”及“向上转型”操作,但某些强制类型转换的错误也许不会被编译器捕捉,而在运行后出现异常,可见强制类型转换存在安全隐患,所以提供了泛型机制。本节就来探讨泛型机制。

11.2.1回顾“向上转型”与“向下转型”

在介绍泛型之前,先来看一个例子。
例11.7   在项目中创建Test类,在该类中使基本类型向上转型为Object类型。实例代码如图所示:

运行结果如图所示:

        在本实例中,Test类中定义了私有的成员变量b,它的类型为Object类型,同时为其定义了相应的setXXX(与 getXXX()方法。在类主方法中,将new Boolean(true)对象作为setB()方法的参数,由于setB()方法的参数类型为Object,这样就实现了“向上转型”操作。同时在调用getB()方法时,将getB()方法返回的 Object对象以相应的类型返回,这个就是“向下转型”操作,问题通常就会出现在这里因为“向上转型”是安全的,而如果进行“向下转型”操作时用错了类型,或者并没有执行该操作,就会出现异常,例如以下代码:

t.setB (new Float (12.3));
Integer f= ( Integer) (t.getB());
System.out.println(f);

        上面代码并不存在语法错误,可以被编译器接受,但在执行时会出现 ClassCastException异常。这样看来,“向下转型”操作通常会出现问题,而泛型机制有效地解决了这一问题。

11.2.2定义泛型类 

Object类为最上层的父类,很多程序员为了使程序更为通用,设计程序时通常使传入的值与返回的值都以Object类型为主。当需要使用这些实例时,必须正确地将该实例转换为原来的类型,否则在运行时将会发生 ClassCastException异常。
在JDK 1.5版本以后,提出了泛型机制。其语法如下:类名<T>
其中,T代表一个类型的名称。

将例11.7  改写为定义类时使用泛型的形式。关键代码如下:

public class Overclass<T> {
//定义泛型类
private T over;
//定义泛型成员变量
public T getover() {
//设置getXXX()方法
return over;}
public void setover (T over){//设置setxxx()方法
this.over =over;}
public static void main(String[] args) {
//实例化一个 Boolean型的对象
overclass<Boolean> over1 = new OverClass<Boolean>();//实例化一个Float型的对象
overclass<Float> over2 - new OverClass<Float>();over1.setover(true);
//不需要进行类型转换
over2.setover(12.3f);
Boolean b = over1.getover(); 
//不需要进行类型转换
Float f =over2.getOver(};
System.out.println (b);
System. out.println(f);
    }
}

         运行上述代码,结果与图11.7所示的结果一致。上面代码中定义类时,在类名后添加了一个<T>语句,这里便使用了泛型机制。可以将OverClass类称为泛型类,同时返回和接受的参数使用T这个类型。最后在主方法中可以使用Over<Boolean>形式返回一个 Boolean型的对象,使用OverClass<Float>形式返回一个Float型的对象,使这两个对象分别调用setOver)方法不需要进行显式“向上转型”操作,setOver()方法直接接受相应类型的参数,而调用getOver()方法时,不需要进行“向下转型”操作,直接将getOver()方法返回的值赋予相应的类型变量即可。

从上面代码可以看出,使用泛型定义的类在声明该类对象时可以根据不同的需求指定<T>真正的类型,而在使用类中的方法传递或返回数据类型时将不再进行类型转换操作,而是使用在声明泛型类对象时“<>”符号中设置的数据类型。
使用泛型这种形式将不会发生ClassCastException异常,因为在编译器中就可以检查类型匹配是否正确。
例如,在项目中定义泛型类。

overclass<Float> over2=new Overclass<Float> ();
over2.setover (12.3f);
// Integer i=over2.getOver();//不能将Float型的值赋予Integer变量

        在上面的代码中,由于over2对象在实例化时已经指定类型为Float,而最后一条语句却将该对象获取出的Float类型值赋予Integer类型,所以编译器会报错。而如果使用“向下转型”操作就会在运行上述代码时发生异常。 

说明:
在定义泛型类时,一般类型名称使用T来表达,而容器的元素使用E来表达,具体的设置读者可以参看JDK 5.0以上版本的API。

11.2.3 泛型的常规用法

1.定义泛型类时声明多个类型
在定义泛型类时,可以声明多个类型。语法如下:Mutioverclass<T1,T2>
Mutioverclass:泛型类名称
其中,T1和T2为可能被定义的类型。这样在实例化指定类型的对象时就可以指定多个类型。例如:
MutiOverclass<Boolean,Float>=new MutiOverclass<Boolean,Float>();2.定义泛型类时声明数组类型
定义泛型类时也可以声明数组类型,下面的实例中定义泛型时便声明了数组类型。
例11.8  在项目中创建ArrayClass类,在该类中定义泛型类声明数组类型。实例代码如图所示:

运行结果如图所示:

        本实例在定义泛型类时声明一个成员数组,数组的类型为泛型,然后在泛型类中相应设置setXXX()与getXXX()方法。
        由此可见,可以在使用泛型机制时声明一个数组,但是不可以使用泛型来建立数组的实例。例如,下面的代码就是错误的:

public class ArrayClass <T>{
// private T[] array=new T[10];
//不能使用泛型来建立数组的实例

说明:
JDK1.7版本中添加了一个新特性:自动推断实例化类型的泛型。所以这样的语法:

Arrayclass<string> a = new Arrayclass< >();//实现类的泛型为空会自动转换为:
Arrayclass<stringz a - new Arrayclass<String>();

3.集合类声明容器的元素
        在第10章中学习了集合类,实际应用中,通过在集合类中应用泛型可以使集合类中的元素类型保证唯一性,这样在运行时就不会产生ClassCastException异常,提高了代码的安全性和可维护性。可以使用K和V两个字符代表容器中的键值和与键值相对应的具体值。
例题11.9  在项目中创建MutiOverClass类,在该类中使用集合类声明容器的元素。实例代码如图所示:

运行结果如图所示:

        其实在例11.9中定义的泛型类MutiOverClass纯属多余,因为在Java中这些集合框架已经都被泛型化了,可以在主方法中直接使用public Map<K,V> m=new HashMap<K,V>();语句创建实例,然后相应调用Map接口中的 put()与 get()方法完成填充容器或根据键名获取集合中具体值的功能。集合中除了HashMap这种集合类型之外,还包括ArrayList、Vector等。表11.2列举了几个常用的被泛型化的集合类。

例11.10  在项目中创建ListClass类,在该类中使用泛型实例化常用集合类。实例代码如图所示:

运行结果如图所示:

        在定义集合对象时,如果没有指定具体的类型,泛型参数<T>的类型默认为<Object>,这时运行程序不会报错,但是会有警告信息。

11.2.4  泛型的高级用法

        泛型的高级用法主要包括通过类型参数T的继承和通过类型通配符的继承来限制泛型类型,另外,开发人员还可以继承泛型类或者实现泛型接口,本节将对泛型的一些高级用法进行讲解。

1.通过类型参数T的继承限制泛型类型
        默认可以使用任何类型来实例化一个泛型类对象,但Java 中也对泛型类实例的类型作了限制,这主要通过对类型参数T实现继承来体现,语法如下:

class 类名称<T extends anyclass>

其中,anyClass指某个接口或类。
使用泛型限制后,泛型类的类型必须实现或继承了anyClass这个接口或类。无论anyClass是接口还是类,在进行泛型限制时都必须使用extends 关键字。
例如,在项目中创建LimitClass类,在该类中限制泛型类型。实例代码如图所示:

         上面代码中,将泛型作了限制,设置泛型类型必须实现List接口。例如,ArrayList和 LinkedList 都实现了List接口,而HashMap没有实现List接口,所以在这里不能实例化 HashMap类型的泛型对象。
        当没有使用extends关键字限制泛型类型时,默认 Object类下的所有子类都可以实例化泛型类对象。如图11.11所示的两个语句是等价的。

2.通过类型通配符的继承限制泛型类型

        在泛型机制中,提供了类型通配符,其主要作用是在创建一个泛型类对象时,限制这个泛型类的类型,或者限制这个泛型类型必须继承某个接口或某个类(或其子类)。要声明这样一个对象可以使用“?”通配符,同时使用extends 关键字来对泛型加以限制。

说明:
        通过对类型参数T实现继承限制泛型类型时,在声明时就进行了限制,而通过对类型通配符实现继承限制泛型类型时,则在实例化时才进行限制。

使用泛型类型通配符的语法如下:

泛型类名称<?extends List> a=null;

        其中,<? extends List>表示类型未知,当需要使用该泛型对象时,可以单独实例化。例如,在项目中创建一个类文件,在该类中限制泛型类型。

A<?extends List> a=null;
a=new A<ArrayList> ();
a=new A<LinkedList>();

        如果实例化没有实现List 接口的泛型对象,编译器将会报错。例如,实例化HashMap对象时,编译器将会报错,因为 HashMap类没有实现List 接口。
        除了可以实例化一个限制泛型类型的实例之外,还可以将该实例放置在方法的参数中。例如,在项目中创建一个类文件,在该类的方法参数中使用匹配字符串。

public void doSomething(A<? extends List> a){
}

在上述代码中,定义方式有效地限制了传入doSomething()方法的参数类型。
        如果使用A<?>这种形式实例化泛型类对象,则默认表示可以将A指定为实例化Object 及以下的子类类型。读者可能对这种编码类型有些疑惑,下面的代码将直观地介绍A<?>泛型机制。

例题11.11―在项目中创建WildClass 类,演示在泛型中使用通配符形式。实例代码如图所示:

运行结果如图所示:

        上面代码中,由于对象11是没有使用A<?>这种形式初始化出来的对象,所以它可以调用set(方法改变集合中的值,但2与l3则是通过使用通配符的方式创建出来的,所以不能改变集合中的值,所以无法调用set()方法;另外,List<?>类型的对象可以接受String 类型的ArrayList集合,也可以接受Integer类型的LinkedList集合,也许有的读者会有疑问,List<?>12=11语句与List 12=11存在何种本质区别?使用通配符声明的名称实例化的对象不能对其加入新的信息,只能获取或删除。

技巧:
泛型类型限制除了可以向下限制之外,还可以向上限制,只要在定义时使用super关键字即可。例如,"A<? superList> a=null;”这样定义后,对象a只接受List接口或上层父类类型,如a=new A<Objec>0;o

3.继承泛型类与实现泛型接口
定义为泛型的类和接口也可以被继承与实现。
例如,在项目中创建一个类文件,在该类中继承泛型类。 

public class Extendclass<T1> {
    }
class Subclass<T1,T2,T3> extends ExtendClass<T1> {//泛型可以比父类多,但不可以比父
类少
    }

        如果在SubClass类继承ExtendClass类时保留父类的泛型类型,需要在继承时指明,如果没有指明,直接使用extends ExtendsClass语句进行继承操作,则SubClass类中的T1、T2和T3都会自动变为Object,所以在一般情况下都将父类的泛型类型保留。
        定义的泛型接口也可以被实现。
        例如,在项目中创建一个类文件,在该类中实现泛型接口。

interface TestInterface<T1> {
    }
class SubClass2<T1,T2,T3> implements TestInterface<T1> {
    }

11.2.5  泛型总结

使用泛型需遵循以下原则:

(1)  泛型的类型参数只能是类类型,不可以是简单类型,如A<int>这种泛型定义就是错误的,

(2)泛型的类型个数可以是多个.
(3)可以使用extends关键字限制泛型的类型。

(4)可以使用通配符限制泛型的类型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值