山脚下(第0004步)——JAVA集合与泛型,第0004步java
山脚下(第0004步)——JAVA集合与泛型,第0004步java
- 一、JAVA集合
- 1、概览
- 2、集合详解
- 2.1、List
- 2.2、Set
- 2.3、Map
- 2.4、Queue
- 二、JAVA泛型
- 1、概述
- 2、泛型的使用
- 2.1、泛型类
- 2.2、泛型接口
- 2.3、泛型方法
- 3、泛型的通配符
- 3.1、无限制通配符 < ?>
- 3.2、上界通配符 < ? extends E>
- 3.3、下界通配符< ? super E>
- 4、泛型的类型擦除
- 5、泛型规则
一、JAVA集合
1、概览
2、集合详解
2.1、List
2.2、Set
2.3、Map
2.4、Queue
二、JAVA泛型
1、概述
所谓的“泛型”是指参数化类型,即将类型由原来的具体类型进行参数化。
类似于方法中的变量参数,此时类型也定义成参数形式(类型形参),然后在使用时传入具体类型(类型实参)。
类型参数的意义是告诉编译器这个集合中要存放实例的类型,从而在添加其他类型时做出提示,在编译时就为类型安全做了保证。
在没有泛型之前,一旦把一个对象“丢进Java集合中”,集合就会忘记对象的类型,把所有的对象当成Object类型处理。当程序从集合中取出对象后,就需要进行强制类型转换,这种强制类型转换不仅代码臃肿,而且容易因为类型转换不匹配而引起ClassCastException异常。Java泛型可以保证如果程序在编译时候没有警告,运行时就不会产生ClassCastException异常。
引入泛型的主要目标有:
- 类型安全
1)泛型的主要目标是提高 Java 程序的类型安全
2)编译时期就可以检查出因 Java 类型不正确导致的 ClassCastException 异常
3)符合越早出错代价越小原则 - 消除强制类型转换
1)泛型的一个附带好处是,使用时直接得到目标类型,消除许多强制类型转换
2)所得即所需,这使得代码更加可读,并且减少了出错机会 - 潜在的性能收益
1)由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改
2)所有工作都在编译器中完成
3)编译器生成的代码跟不使用泛型(和强制类型转换)时所写的代码几乎一致,只是更能确保类型安全而已
2、泛型的使用
2.1、泛型类
所谓的“泛型类”是指在类的定义中使用泛型类型。通过泛型,可以对一组类的操作对外开放相同的接口。最典型的应用就是各种容器类,如:List、Set、Map。
格式
class className <泛型标识(可多个,逗号隔开)>{
private 泛型标识 varName;
......
public 泛型标识 methodName(){
......
return 泛型标识
}
public void methodName(泛型标识 varName){
......
}
......
}
其中,“泛型标识” 可以任意标识,如T,K,E......
实例
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
......
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
......
public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
......
final Entry<K,V> getEntry(Object key) {
......
}
public V put(K key, V value) {
......
}
}
注意:
1)泛型的类型参数只能是引用类类型(包括自定义类),不能是简单类型;
2)在使用泛型时:
如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型会起到本应起到的限制作用;
如果不传入泛型实参,则在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型;
2.2、泛型接口
所谓的“泛型接口”是指在接口的定义中使用泛型类型。泛型接口与泛型类的定义及使用基本相同。
格式
interface interfaceName <泛型标识(可多个,逗号隔开)>{
private 泛型标识 varName;
......
public 泛型标识 methodName();
public void methodName(泛型标识 varName);
......
}
其中,“泛型标识” 可以任意标识,如T,K,E......
实例
public interface Map<K,V> {
......
V get(Object key);
V put(K key, V value);
V remove(Object key);
......
interface Entry<K,V> {
K getKey();
V getValue();
......
}
}
注意:如果在定义实现给定泛型接口的实现类或抽象类时未传入泛型实参,则在定义实现类或抽象类时,需将泛型的声明也加入到定义中,即泛型类。
2.3、泛型方法
所谓的“泛型方法”是指使用泛型的方法。
泛型方法的声明中,在修饰符和返回值之间必须有“<泛型标识(可多个,逗号隔开)>”。
格式
public <泛型标识(可多个,逗号隔开)> 返回值(泛型标识) methodName(泛型标识 varName) {
......
}
注意:
1)“<泛型标识(可多个,逗号隔开)>”指明了泛型方法中可以使用的泛型类型,泛型方法中使用的泛型标识都必须在此声明,介于修饰符和返回值之间。它是泛型方法的必须部分。
1)泛型方法中可以不使用泛型类或接口指定的泛型标识,而是泛型方法自身的泛型标识。如:
public class Fruit<E> {
......
public <T> T getColor(T obj){
......
}
}
2)可变参数:可变参数的类型也可以泛型类型。如:
public <T> void getColor(T... args){
......
}
3)静态方法的泛型方法
如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法。因为静态方法无法访问泛型类的泛型类型。如:
public class Fruit<E> {
......
//错误
public static void show(E e) {
......
}
//正确
public static <T> void show(T t){
......
}
}
注意:
1)无论何时,如果条件允许,应尽量使用泛型方法。也就是说,如果使用泛型方法满足将整个类泛型化,那么就应该使用泛型方法;
2)如果static方法要使用泛型能力,就必须使其成为泛型方法;
3、泛型的通配符
Java泛型有如下三种通配符:
1)< ?>:无限制通配符
2)< ? extends E>:extends关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类
3)< ? super E>:super关键字声明了类型的下界,表示参数化的类型可能是指定的类型,或者是此类型的父类
3.1、无限制通配符 < ?>
类型通配符一般是使用“?”代替具体的类型实参。简单地说,?和Number、String、Integer一样,都是一种实际的类型。我们可以把“?”看成所有类型的父类,是一种真实的类型。
例如:
public void show(Class<?> obj){
......
}
注意:
1)此处“?”是类型实参,而不是类型形参。
2)当具体类型不确定的时候,可以使用通配符“?”;
3.2、上界通配符 < ? extends E>
在类型参数中使用 extends 表示这个泛型中的参数必须是 E 或者 E 的子类,这样有两个好处:
1)如果传入的类型不是 E 或者 E 的子类,编辑不成功
2)泛型中可以使用 E 的方法,要不然还得强转成 E 才能使用
例如:
/**
* 有限制的通配符之 extends (有上限),表示参数类型 必须是 BookBean 及其子类,更灵活
*/
private <K extends ChildBookBean, E extends BookBean> E test2(K arg1, E arg2){
E result = arg2;
arg2.compareTo(arg1);
//.....
return result;
}
3.3、下界通配符< ? super E>
在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。
例如:
// dst 类型 “大于等于” src 的类型,这里的“大于等于”是指 dst 表示的范围比 src 要大,因此装得下 dst 的容器也就能装 src。
private <E> void add(List<? super E> dst, List<E> src){
for (E e : src) {
dst.add(e);
}
}
通配符比较:
1)无限制通配符 < ?> 和 Object 有些相似,用于表示无限制或者不确定范围的场景。
2)< ? super E> 用于灵活写入或比较,使得对象可以写入父类型的容器,使得父类型的比较方法可以应用于子类对象。
3)< ? extends E> 用于灵活读取,使得方法可以读取 E 或 E 的任意子类型的容器对象通配符使用规则:
1)如果参数化类型表示一个 T 的生产者,使用 < ? extends T>;
2)如果它表示一个 T 的消费者,就使用 < ? super T>;
3)如果既是生产又是消费,那使用通配符就没什么意义了,因为你需要的是精确的参数类型。
4、泛型的类型擦除
《待完善……》
5、泛型规则
1)泛型的参数类型只能是类(包括自定义类),不能是简单类型;
2)同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的;
3)泛型的类型参数可以有多个;
4)泛型的参数类型可以使用 extends 语句,习惯上称为“有界类型”;
5)泛型的参数类型还可以是通配符类型,例如 Class ;
相关文章
- 暂无相关文章
用户点评