第十一章 枚举与泛型

11.1枚举


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

11.1.1使用枚举类型设置常量


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

public interface Constants{
public static final int Constants_A=1;//定义一个泛型变量并赋初值
 public static final int Conostants_B=12;
}


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

public enum Constants{
Constants_A,//泛型常量
Constants_B,
Constants_c
}


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

interface Constants { // 将常量放置在接口中
    public static final int Constants_A = 1;
    public static final int Constants_B = 12;
}
 
public class test12{
    enum Constants2 { // 将常量放置在枚举类型中
        Constants_A, Constants_B
    }
 
    // 使用接口定义常量
    public static void doit(int c) { // 定义一个方法,这里的参数为int型
        switch (c) { // 根据常量的值做不同操作
        case Constants.Constants_A:
            System.out.println("doit() Constants_A");
            break;
        case Constants.Constants_B:
            System.out.println("doit() Constants_B");
            break;
        }
    }
 
    public static void doit2(Constants2 c) { // 定义一个参数对象是枚举类型的方法
        switch (c) { // 根据枚举类型对象做不同操作
        case Constants_A:
            System.out.println("doit2() Constants_A");
            break;
        case Constants_B:
            System.out.println("doit2() Constants_B");
            break;
        }
    }
 
    public static void main(String[] args) {
        test12.doit(Constants.Constants_A); // 使用接口中定义的常量
        test12.doit2(Constants2.Constants_A); // 使用枚举类型中的常量
        test12.doit2(Constants2.Constants_B); // 使用枚举类型中的常量
        test12.doit(3);
        // ConstantsTest.doit2(3);
    }
}

运行结果:

 

 11.1.2深入了解枚举类型


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


枚举类型较传统定义常量的方式,除了具有参数类型检测的优势之外,还具有其他方面的优势。用户可以将一个枚举类型看作是一个类,它继承于java. lang. Enum类,当定义一个枚举类型时,每一个枚举类型成员都可以看作是枚举类型的一个实例,这些枚举类型成员都默认被final、public、static修饰,所以当使用枚举类型成员时直接使用枚举类型名称调用枚举类型成员即可。由于枚举类型对象继承于java. lang. Enum类,所以该类中一些操作枚举类型的方法都可以应用到枚举类型中。表11.1中列举了枚举类型中的常用方法。

方法名称具体含义使用方法
values()该方法可以将枚举类型成员以数组的形式返回枚举类型名称values()
valuesOf()该方法可以实现将普通字符串转换成枚举实例枚举类型名称valuesOF("abc")
compareTo()该方法用于比较两个枚举对象在定义时的顺序枚举对象compareTo()
Ordinal该方法用于得到枚举成员的位置索引枚举对象Ordinal

 1.values():将枚举类型成员以数组的形式返回。

import static java.lang.System.out;
 
public class test12 {
    enum Constants2 { // 将常量放置在枚举类型中
        Constants_A, Constants_B
    }
 
    // 循环由values()方法返回的数组
    public static void main(String[] args) {
        for (int i = 0; i < Constants2.values().length; i++) {
            // 将枚举成员变量打印
            out.println("枚举类型成员变量:" + Constants2.values()[i]);
        }
    }
}

运行结果:

 2.valueOf()与compareTo()


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

 

import static java.lang.System.out;
 
public class test12 {
    enum Constants2 { // 将常量放置在枚举类型中
        Constants_A, Constants_B, Constants_C, Constants_D
    }
 
    // 定义比较枚举类型方法,参数类型为枚举类型
    public static void compare(Constants2 c) {
        // 根据values()方法返回的数组做循环操作
        for (int i = 0; i < Constants2.values().length; i++) {
            // 将比较结果返回
            out.println(c + "与" + Constants2.values()[i] + "的比较结果为:" + c.compareTo(Constants2.values()[i]));
        }
    }
 
    // 在主方法中调用compare()方法
    public static void main(String[] args) {
        compare(Constants2.valueOf("Constants_B"));
    }
}

 

运行结果:

 3.ordinal():获取枚举对象的位置索引值。

import static java.lang.System.out;
 
public class test12 {
    enum Constants2 { // 将常量放置在枚举类型中
        Constants_A, Constants_B, Constants_C
    }
 
    public static void main(String[] args) {
        for (int i = 0; i < Constants2.values().length; i++) {
            // 在循环中获取枚举类型成员的索引位置
            out.println(Constants2.values()[i] + "在枚举类型中位置索引值" + Constants2.values()[i].ordinal());
        }
    }
}


运行结果:

 

 2.枚举类型中的构造方法

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

import static java.lang.System.out;
 
public class test12 {
    enum Constants2 { // 将常量放置在枚举类型中
        Constants_A("我是枚举成员A"), // 定义带参数的枚举类型成员
        Constants_B("我是枚举成员B"), Constants_C("我是枚举成员C"), Constants_D(3);
        private String description;
        private int i = 4;
 
        private Constants2() {
        }
 
        // 定义参数为String型的构造方法
        private Constants2(String description) {
            this.description = description;
        }
 
        private Constants2(int i) { // 定义参数为int型的构造方法
            this.i = this.i + i;
        }
 
        public String getDescription() { // 获取description的值
            return description;
        }
 
        public int getI() { // 获取i的值
            return i;
        }
    }
 
    public static void main(String[] args) {
        for (int i = 0; i < Constants2.values().length; i++) {
            out.println(Constants2.values()[i] + "调用getDescription()方法为:" + Constants2.values()[i].getDescription());
        }
        out.println(Constants2.valueOf("Constants_D") + "调用getI()方法为:" + Constants2.valueOf("Constants_D").getI());
    }
}

 

运行结果:

import static java.lang.System.out;
 
interface d {
    public String getDescription();
 
    public int getI();
}
 
public enum test12 implements d {
    Constants_A { // 可以在枚举类型成员内部设置方法
        public String getDescription() {
            return ("我是枚举成员A");
        }
 
        public int getI() {
            return i;
        }
    },
    Constants_B {
        public String getDescription() {
            return ("我是枚举成员B");
        }
 
        public int getI() {
            return i;
        }
    },
    Constants_C {
        public String getDescription() {
            return ("我是枚举成员C");
        }
 
        public int getI() {
            return i;
        }
    },
    Constants_D {
        public String getDescription() {
            return ("我是枚举成员D");
        }
 
        public int getI() {
            return i;
        }
    };
    private static int i = 5;
 
    public static void main(String[] args) {
        for (int i = 0; i < test12.values().length; i++) {
            out.println(test12.values()[i] + "调用getDescription()方法为:" +test12.values()[i].getDescription());
            out.println(test12.values()[i] + "调用getI()方法为:" +test12.values()[i].getI());
        }
    }
}

运行结果:

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


11.1.3 使用枚举类型的优势

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


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

(4)运行效率高。

11.2泛型


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

11.2.1回顾向上转型和向下转型

public class test12 {
    private Object b; // 定义Object类型成员变量
 
    public Object getB() { // 设置相应的getXXX()方法
        return b;
    }
 
    public void setB(Object b) { // 设置相应的setXXX()方法
        this.b = b;
    }
 
    public static void main(String[] args) {
        test12 t = new test12();
        t.setB(new Boolean(true)); // 向上转型操作
        System.out.println(t.getB());
        t.setB(new Float(12.3));
        Float f = (Float) (t.getB()); // 向下转型操作
        System.out.println(f);
    }
}

运行结果:

 

 11.2.2定义泛型类


Object类为最上层的父类,很多程序员为了使程序更为通用,设计程序时通常使传入的做回的值都以Object类型为主。当需要使用这些实例时,必须正确地将该实例转换为原来的类型.则在运行时将会发生ClassCastException异常。

类名<T>
public class test12<T> {
    private T[] array; // 定义泛型数组
 
    public void SetT(T[] array) { // 设置SetXXX()方法为成员数组赋值
        this.array = array;
    }
 
    public T[] getT() { // 获取成员数组
        return array;
    }
 
    public static void main(String[] args) {
test12<String> a = new test12<String>();
        String[] array = { "成员1", "成员2", "成员3", "成员4", "成员5" };
        a.SetT(array); // 调用SetT()方法
        for (int i = 0; i < a.getT().length; i++) {
            System.out.println(a.getT()[i]); // 调用getT()方法返回数组中的值
        }
    }
}

运行结果:

 

11.2.3泛型的常规用法


1.定义泛型类时声明多个类型

在定义泛型类时,可以声明多个类型。

Class<T1,T2>
Class:泛型类名称


 2.定义泛型类时声明数组类型

import java.util.HashMap;
import java.util.Map;
 
public class test12<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) {
        // 实例化泛型类对象
test12<Integer, String> mu = new test12<Integer, String>();
        for (int i = 0; i < 5; i++) {
            // 根据集合的长度循环将键名与具体值放入集合中
            mu.put(i, "我是集合成员" + i);
        }
        for (int i = 0; i < mu.m.size(); i++) {
            // 调用get()方法获取集合中的值
            System.out.println(mu.get(i));
        }
    }
}

运行结果:

 

 3.集合类声明容器的元素

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
 
public class ListClass {
    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("获取ArrayList容器的值:" + a.get(i));
        }
        // 定义HashMap容器,设置容器的键名与键值类型分别为Integer与String型
        Map<Integer, String> m = new HashMap<Integer, String>();
        for (int i = 0; i < 5; i++) {
            m.put(i, "成员" + i); // 为容器填充键名与键值
        }
        for (int i = 0; i < m.size(); i++) {
            // 根据键名获取键值
            System.out.println("获取Map容器的值" + m.get(i));
        }
        // 定义Vector容器,使容器中的内容为String型
        Vector<String> v = new Vector<String>();
        for (int i = 0; i < 5; i++) {
            v.addElement("成员" + i); // 为Vector容器添加内容
        }
        for (int i = 0; i < v.size(); i++) {
            // 显示容器中的内容
            System.out.println("获取Vector容器的值" + v.get(i));
        }
    }
}

 

运行结果:

 其实在例11.9中定义的泛型类&nbspMutiOverClass&nbsp纯属多余,因为在Java中这些集合框架已经都被泛发化了,可以在主方法中直接使用public Map<K、V>m=&nbspnewHashMap&nbsp<K.V>0;语句创建实例。然后题的用Map接口中的put()与get(方法完成填充容器或根据键名获取集合中具体值的功能,集合中需Hash Map这种集合类型之外,还包括Array List、Vector等。

集合类泛型定义
ArrayListArrayList<E>
HashMapHashMap<K,V>
HashSetHashSet<E>
VectorVector<E>

 11.2.4泛型的高级用法


1.通过类型参数T的继承限制泛型类型

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

class 类名称 <T extends anyClass>


anyClass:接口或者类

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class Limitclass<T extends List> {//限制泛型的类型
public static void main (string[] args) {//可以实例化已经实现List接口的类
Limitclass<ArrayList> 11 = new LimitClass<ArrayList>();
Limitclass<LinkedList>12 = new Limitclass<LinkedList>();
//这句是错误的,因为HashMap没有实List()接口
//LimitClass<HashMap> 13=new Limitclass<HashMap>();


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

 

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 类,演示在泛型中使用通配符形式。
 

package javaiselevensection;
 
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
 
public class WildClass {
    public static void main(String[] args) {
        List<String> l1 = new ArrayList<String>(); // 创建一个ArrayList对象
        l1.add("成员"); // 在集合中添加内容
        List<?> l2 = l1; // 使用通配符
        List<?> l3 = new LinkedList<Integer>();
        System.out.println("l1:" + l1.get(0)); // 获取l1集合中第一个值
        System.out.println("l2:" + l2.get(0)); // 获取l2集合中第一个值
        l1.set(0, "成员改变"); // 没有使用通配符的对象调用set()方法
        // l2.set(0, "成员改变"); // 使用通配符的对象调用set()方法,不能被调用
        // l3.set(0, 1);
        System.out.println("l1:" + l1.get(0)); // 可以使用l1的实例获取集合中的值
        System.out.println("l2:" + l2.get(0)); // 可以使用l2的实例获取集合中的值
    }
}


 

 

上面代码中,由于对象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>{
}


小结

使用泛型需遵循以下原则。
(1 )泛型的类型参数只能是类类型,不可以是简单类型,如A<int>这种泛型定义就是错误的,

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值