《Java从入门到精通》第17章笔记:枚举类型和泛型
17 枚举类型与泛型
枚举类型可以取代以往常量的定义方式,即将常量封装在类或接口中,此外,它还提供了安全检查功能。枚举类型本质上还是以类的形式存在。泛型的出现不仅可以让程序员少写某些代码,主要的作用是解决类型安全问题,它提供编译时的安全检查,不会因为将对象置于某个容器中而失去其类型。本章将着重讲解枚举类型与泛型。
目标:
- 掌握枚举类型
- 掌握泛型
17.1 枚举类型
17.1.1 使用枚举类型设置常量
以往定义常量在接口中直接定义,可以直接使用,且该常量不能被修改。
例:
public interface Constants {
public static final int Constants_A = 1;
}
枚举类型出现后,逐渐取代了这种常量定义方式。使用枚举类型定义常量的语法如下:
public enum Constants {
Constants_A;
}
enum是定义枚举类型关键字。使用该常量时:Constants.Constants_A。
17.1.2 深入了解枚举类型
- 操作枚举类型成员的方法
枚举类型较传统定义常量的方式,除了参数类型检测的优势,还有其他。
可将枚举类型看作类,定义一个枚举类型,每一个枚举类型成员都可看做枚举类型的实例,这些枚举类型成员都默认被final、public、static修饰,所以当使用枚举类型成员时直接使用枚举类型名称调用枚举类型成员即可。
- 枚举类型中的构造方法
可添加构造方法,但必须为private修饰。
注:这可防止客户代码实例化一个枚举对象。
17.1.3 使用枚举类型的优势
特点:
- 类型安全。
- 紧凑有效的数据定义。
- 可以和程序其他部分完美交互。
- 运行效率高。
17.2 泛型
17.2.1 回顾向上转型和向下转型
public class Test {
private Object b;
public Object getB() {
return b;
}
public void setB(Object b) {
this.b = b;
}
public static void main(String[] args){
Test t = new Test();
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);
}
}
向下转型不存在语法错误,所以被编译器接受,但在执行时会出现ClassCastException异常。
17.2.2 定义泛型类
语法:
类名,T代表一个类型的名称。
例:
public class OverClass<T> {
private T over;
public T getOver() {
return over;
}
public void setOver(T over) {
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);
}
}
返回和接受的参数使用T这个类型。使用setOver()方法不需要显示向上转型,同理,getOver()不需要向下转型。
由上例可看出,使用泛型定义的类在声明该类对象时可以根据不同的需求指定真正的类型,而在使用类中的方法传递或返回数据类型时将不再需要进行类型转换操作,而是使用在声明泛型类对象时“<>”符号中设置的数据类型。
使用翻译不会发生ClassCastExceptio异常,因为在编译器中就可以检查类型匹配是否正确。
17.2.3 泛型的常规用法
- 定义泛型类时声明多个类型
语法:
MutiOverClass<T1, T2>;
MutiOverClass.泛型类名称
其中,T1和T2为可能被定义的类型。
这样在实例化指定类型的对象时就可以指定多个类型。例如:
MutiOverClass<Boolean, Float> = new MutiOverClass<Boolean, Float>(); - 定义泛型类时声明数组类型
实例:
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> a = new ArrayClass<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]);//调用geT()方法返回数组中的值
}
}
}
在定义泛型类时声明一个成员数组,数组的类型为泛型,然后再泛型类中响应设置setXXX()与getXXX()方法。
可以使用泛型机制申明数组,但是不可以使用泛型建立数组实例。例如:下面的代码是错误的。
public class ArrayClass<T> {
//private T[] array = new T[10];//不能用泛型来建立数组的实例
}
- 集合类声明容器的元素
可以使用K和V两个字符代表容器中的简直和与键值相对应的具体值。
public class MutiOverClass<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) {
//实例化泛型类对象
MutiOverClass<Integer, String> mu = new MutiOverClass<Integer, String>();
for(int i = 0; i < 5; i++) {
//根据集合的长度循环将键名与具体值放入集合中
mu.put(i, "我是集合成员" + i);
}
for(int i = 0; i < nu.m.size(); i++) {
//调用get()方法获取集合中的值
System.out.println(mu.get(i));
}
}
}
17.2.4 泛型的高级用法
泛型的高级用法包括限制泛型可用类型和使用类型通配符。
- 限制泛型可用泛型
默认可以使用任何类型来实例化一个泛型类对象,但Java中也对泛型类实例的类做了限制。语法如下:
class 类名称
其中,anyClass指某个接口或类。
使用泛型限制后,泛型类的类型必须实现或继承了anyClass这个接口或类。无论anyClass是接口还是类,在进行泛型限制时都必须使用extends关键字。
public class LimitClass<T extends List> {//限制泛型的类型
public static void main(String[] args){
//可以实例化已经实现List接口的类
LimitClass<ArrayList> l1 = new LimitClass<ArrayList>();
LimitClass<LinkedList> l1 = new LimitClass<LinkedList>();
//这句是错误的,因为HashMap没有实现List()接口
//LimitClass<HashMap> l3 = new LimitClass<HashMap>();
}
}
public class a{}和public class a()等价。
2. 使用类型通配符
语法:
泛型类名称<? extends List> a = null;
例:
A<? extends List> a = null;
a = new A<ArrayList>();
a = new A<LinkedList>();
如果实例化没有实现List接口的泛型对象,编译器将会报错。例如,实例化HashMap对象时,编译器将会报错,因为HashMap类没有实现List接口。
除了可以实例化一个限制泛型类型的实例外,还可将该实例放置在方法的参数中。例:
public void doSomething(<? extends List> a) {
}
定义方式有效限制了传入doSomething()方法的参数类型。
技巧:泛型类型限制除了可以向下限制外,还可进行向上限制,只要在定义时使用super关键字即可。例如,“A<? super List> a = null;”这样定义后,对象a只接受List接口或上层父类类型,如“a = new A();”。
3. 继承泛型类对实现泛型接口
定义为泛型的类和接口也可以被继承与实现。
public class ExtendClass<T1> {
}
class SubClass<T1, T2, T3> extends ExtendClass<T1> {
}
17.2.5 泛型总结
特点:
- 泛型的类型参数只能是类类型,不可以是简单类型,如A这种泛型定义就是错误的;
- 泛型的类型个数可以是多个;
- 可以使用extends关键字限制泛型的类型;
- 可以使用通配符限制泛型的类型。