泛型的概念
泛型是指允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定,即传入实际的类型参数,也称为类型实参。
例如:Collection< E >,List< E >,ArrayList< E > 这个< E >就是类型参数,即泛型。
泛型在集合中的使用
- 集合中有无泛型对比
(1)没有泛型时
1)任何类型的对象都可以添加到集合中,类型不安全。
2)读取出来的对象需要强转,操作繁琐,并且可能出现ClassCastException异常。
(2)有泛型时
1)只有指定类型的数据可以添加到集合中。
2)读取出的对象不需要强转,操作便捷。
举例:
//没有使用泛型
public void test(){
ArrayList list = new ArrayList();
list.add(123);
//类型不安全
List.add("123");
List.add("Java");
for(Object obj: list){
//强转时,可能出现ClassCastException
System.out.println( (Integer) obj);
}
}
//使用泛型时
public void test2(){
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(78);
list.add(87);
list.add(99);
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
- 集合中使用泛型总结
(1)在实例化集合类时,可以指明具体的泛型类型。指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。比如:add(E e) ,实例化以后:add(Integer e)。
(2)泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换。
(3)如果实例化时,没指明泛型的类型,默认类型为java.lang.Object类型。
(4)如果泛型结构是一个接口或者抽象类,则不可创建泛型类的对象。
(5)泛型的指定中不能使用基本数据类型,可以使用包装类替换。
(6)在静态方法中不能使用类的泛型。因为静态方法随着类的加载而加载,而泛型是在实例化对象时才确定。
(7)泛型方法可以声明为静态的。原因:泛型参数是在调用方法时确定的,并非在实例化类时确定。
(8)异常类不能是泛型的。
(9)父类中有泛型,子类中可以选择保留父类的泛型,也可以增加自己的泛型类型。
自定义泛型类,泛型接口,泛型方法
- 自定义泛型类(举例)
public class Order<T> {
String orderName;
int orderId;
T orderT;
public Order(){
T[] arr = (T[]) new Object[10];
}
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 <E> List<E> copyFromArrayToList(E[] arr){
ArrayList<E> list = new ArrayList<>();
for(E e : arr){
list.add(e);
}
return list;
}
- 注意点
(1)泛型可能有多个参数,此时应将多个参数一起放在尖括号内。比如:< E1, E2, E3 >。
(2)泛型不同的引用不能相互赋值。
(3)泛型如果不指定将会被擦除,泛型对应的类型均按照Object处理,但不等价于Object。