泛型
什么是泛型?
集合容器类在设计或声明阶段不能确定这个容器到底实际存的是什么类型的对象,在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。
所谓泛型 :就是允许在定义类、接口时通过一个表示类中某个属性的类型或者时某个方法的返回值及参数类型。这个类型将在使用时(如:继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数)。
把元素的类型设计成一个参数,这个类型参数就叫做泛型。【相当于标签】
泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数。
Collection<E>,List<E>,ArrayList<E> ,<E>就是类型参数,即泛型。
为什么要有泛型?
- 解决元素存储的安全性问题;
- 解决获取数据元素时,需要类型强制准换的问题。
在集合中没有泛型时:
示例代码:
//在集合中使用泛型之前的情况:
public static void test1(){
ArrayList list = new ArrayList();
//存放学生的成绩
list.add(89);
list.add(78);
list.add(88);
list.add(97);
//问题1:类型不安全
list.add("Tom");
for (Object score :list){
//问题2:强制类型转换时,有可能出现类型转换异常
int studentScore = (Integer) score;
System.out.println(studentScore);
}
}
在进行强转时,String类型不能转为Integer类型,抛出ClassCastException
使用Object可以存储指向任意类型对象的引用到顺序表中:
public class MyArrayList {
private Object[] array;//保存顺序表的元素,即Object类型的引用
private int size;//保存顺序表内数据个数
public void add(Object o){
//尾插
}
public Object get(int index){
//获取index位置的元素
}
}
添加元素:
MyArrayList books = new MyArrayList();
for (int i = 0; i < 10; i++) {
books.add(new Book()); // 尾插 10 本书到顺序表
}
MyArrayList people = new MyArrayList();
for (int i = 0; i < 10; i++) {
people.add(new Person());//尾插10个人到顺序表
}
问题:现在的 MyArrayList 虽然可以做到添加任意类型的引用到其中了,但遇到以下代码就会产生问题。
MyArrayList books = new MyArrayList();
books.add(new Book());
//将Object 类型转换为Person类型,需要类型转换才能成功
//编译正确,但运行时会抛出异常 ClassCastException
Person person = (Person)books.get(0);
在集合中使用泛型
- 集合接口或集合类在jdk1.5时都修改为带泛型的结构;
- 在实例化集合类时,可以指明具体的泛型类型;
- 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(如:方法、构造器)使用到类的泛型的位置,都指定为实例化时的泛型类型;
- 注意:泛型的类型必须是一个类,不能是基本数据类型类型。需要用到基本数据类型的位置,拿包装类来替换;
- 如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型
/**
* 在集合中使用泛型的情况:以ArrayList为例
*/
public static void test2() {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(78);
list.add(89);
list.add(97);
list.add(76);
//编译时,就会进行类型检查,保证数据的安全
//list.add("Tom");
//方式一:
for (Integer score:list){
//避免了强转的操作,就不会出现类型转换异常
int stuScore = score;
System.out.println(stuScore);
}
//方式二:迭代器
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
/**
* 在集合中使用泛型的情况:以HashMap为例
*/
public static void test3(){
Map<String,Integer> map = new HashMap<>();
map.put("Tom",87);
map.put("Jack",89);
map.put("Mike",78);
//map.put(123,"ABC");
//泛型的嵌套
Set<Map.Entry<String,Integer>> entries = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
while (iterator.hasNext()){
Map.Entry<String, Integer> e = iterator.next();
String key = e.getKey();
Integer value = e.getValue();
System.out.println(key+"------"+value);
}
}
自定义泛型结构
泛型类、泛型接口
泛型类是在实例化的时候指明泛型的具体类型。
- 泛型类可能有多个参数,此时多个参数应该一起放在<>内。
- 泛型类的构造器:public GenericClass(){}。
- 实例化时,操作原来泛型位置的结构必须与指定的泛型类型一致
- 泛型不同的引用不能相互赋值。【在编译时ArrayList<Integer>和ArrayList<String>是两种类型,但是在运行时只有一个ArrayList被加载到JVM中】
- 如果泛型结构是一个接口或抽象类,不能创建泛型类的对象。
- 在静态方法中不能使用类的泛型
- 异常类不能声明为泛型类
//自定义一个泛型类
public class Order<T>{
String orderName;
int orderId;
//类的内部结构就可以使用类的泛型
T orderT;
public Order(){
}
public Order(String orderName, int orderId, T orderT) {
this.orderName = orderName;
this.orderId = orderId;
this.orderT = orderT;
}
public T getOrderT() {
return orderT;
}
public void setOrderT(T orderT) {
this.orderT = orderT;
}
@Override
public String toString() {
return "Order{" +
"orderName='" + orderName + '\'' +
", orderId=" + orderId +
", orderT=" + orderT +
'}';
}
//在静态方法中不能使用类的泛型
// public static void show(T orderT){
// System.out.println(orderT);
// }
}
//自定义泛型类、泛型接口测试
public class GenericTest1 {
public void test1(){
//如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object类型
//如果定义的类是带泛型的,建议在实例化时就要指明类的泛型
/*Order order = new Order();
order.setOrderT(123);
order.setOrderT("ABC");*/
//实例化时指明类的泛型
Order<String > order = new Order<>("orderAA",1001,"order:AA");
order.setOrderT("AA:hello");
}
public void test2(){
SubOrder sub1 = new SubOrder();
//由于子类在继承带泛型的父类时,指明了泛型类型
//所以实例化子类对象时,不再需要指明泛型
sub1.setOrderT(1122);
SubOrder1<String> sub2 = new SubOrder1<>();
sub2.setOrderT("order2...");
}
public void test3(){
ArrayList<String > list1 = null;
ArrayList<Integer> list2 = new ArrayList<>();
//泛型不同的引用不能相互赋值
//list1 = list2;
}
}
public class SubOrder extends Order<Integer>{
}
public class SubOrder1<T> extends Order<T>{
}
泛型方法
泛型方法是在调用方法的时候指明泛型的具体类型。
定义泛型方法
定义泛型方法时,必须再返回之前加一个<T>,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。
调用泛型方法
泛型方法可以声明为静态的。
原因:泛型参数是在调用方法时确定的,而不是在实例化类时确定。
示例代码:
//泛型方法;在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系
//泛型方法所属的类是不是泛型类都没有关系
public static <T> List<T> copyFromArrayToList(T[] arr){
ArrayList<T> list = new ArrayList<>();
for (T t:arr){
list.add(t);
}
return list;
}
//测试泛型方法
public void test4(){
Order<String> order = new Order<>();
Integer[] arr = new Integer[]{1,2,3,4};
//泛型方法在调用时,指明泛型参数的类型
List<Integer> list = order.copyFromArrayToList(arr);
System.out.println(list);
}
泛型在继承方面的体现
如果类A是类B的父类,G<A> 和G<B>, 二者不具备子父类的关系,二者是并列关系 如果类A是类B的父类,A<G> 是B<G>的父类
public void test1(){
Object obj = null;
String str = null;
obj = str;
Object[] arr1 = null;
String[] arr2 = null;
arr1= arr2;
List<Object> list1 = null;
List<String> list2 = null;
//此时list1和list2不具有子父类的关系
// list1 = list2;//编译不通过
}
public void test2(){
AbstractList<String> list1 = null;
List<String> list2 = null;
ArrayList<String> list3 = null;
list1 = list3;
list2 = list3;
List<String> list4 = new ArrayList<>();
}
通配符的使用
通配符: ?
类A是类B的父类,G<A> 和G<B>是没有关系的,它们的共同父类是:G<?>
public void test3(){
List<Object> list1 = null;
List<String> list2 = null;
List<?> list = null;
list = list1;
list = list2;
//print(list1);
//print(list2);
List<String > list3 = new ArrayList<>();
list3.add("A");
list3.add("B");
list3.add("C");
list = list3;
//添加:对于List<?>就不能向其内部添加数据
//除了添加null之外
//list.add("D");//编译错误
list.add(null);
//获取:允许读取数据,读取的数据类型为Object
Object o = list.get(0);
}
public void print(List<?> list){
Iterator<?> iterator = list.iterator();
while (iterator.hasNext()){
Object obj = iterator.next();
System.out.println(obj);
}
}
有限制条件的通配符的使用
? extends A: G <? extends A> 可以作为G<A>和G<B>的父类的,其中B是A的子类 ? super A: G<? super A> 可以作为G<A>和G<B>的父类的,其中B是A的父类
public void test4(){
//<=
List<? extends Person> list1 = null;
//>=
List<? super Person> list2 = null;
List<Student> list3 = new ArrayList<>();
List<Person> list4 =new ArrayList<>();
List<Object> list5 =new ArrayList<>();
list1 = list3;
list1 = list4;
//list1 = list5;
// list2 = list3;
list2 = list4;
list2 = list5;
//读取数据:
list1 = list4;
Person person = list1.get(0);
//Student s = list1.get(0);//编译不通过
list1 = list3;
Person person1 = list1.get(1);
list2 = list4;
Object object = list2.get(0);
//写数据
//list1.add(new Student());//编译不通过
//编译通过
list2.add(new Person());
list2.add(new Student());
}
Java类库中的泛型
所有的标准集合接口都是泛型化的—— Collection<V>、List<V>、Set<V> 和 Map<K,V>。
类似地,集合接口的实现都是用相同类型参数泛型化的,HashMap<K,V> 实现 Map<K,V> 等。