1 泛型入门
参数形参(又简称形参)和类型形参
1.1 编译时期不检查类型异常
public class ListErr
{
public static void main(String[] args)
{
//未指定类型,所有被添加进去的对象都会被当做Object处理
List list=new ArrayList();
list.add("a");
list.add("a");
list.add("a");
list.add(5);
for(Object object : list)
{
String str=(String) object;
}
}
}
1.2 手动实现编译时期检查类型
public class StrList
{
private List list = new ArrayList();
public boolean addStr(String str)
{
return list.add(str);
}
public String get(int index)
{
return (String) list.get(index);
}
public int size()
{
return list.size();
}
}
1.3 使用泛型
public static void main(String[] args)
{
List<String> list = new ArrayList<>();
list.add("s");
list.add("s");
list.add("s");
// list.add(5);
for(String string : list)
{
//无需再进行类型转换
String str=string;
}
}
1.4 java7泛型的“菱形”语法
优化了菱形语法,无需进行重复的类型指定;
2 深入泛型
2.1 定义泛型接口、类
允许在定义接口、类时声明类型形参,类型形参在整个接口、类体内可以当成类型使用,几乎所有可以使用普通类型的地方都可以使用这种类型形参;
code:
public class Apple<T>
{
private T info;
public Apple(T info)
{
this.info = info;
}
public T getInfo()
{
return info;
}
public static void main(String[] args)
{
Apple<String> apple = new Apple<>("苹果");
System.out.println(apple.getInfo() + "");
Apple<Double> apple2=new Apple<>(5.34);
System.out.println(apple2.getInfo());
}
}
2.2 从泛型类派生子类
当要实现或者继承或者使用某一个泛型接口、类的时候,此时必须不能再由类型形参,而应该制定类型的确定类型;
2.3 并不存在泛型类
不管泛型类的类型形参传入了什么类型,对于java来说,他们依然是同一个类,因此不能在静态方法,静态初始化块,静态变量和初始化中不允许使用类型形参;
并且由于系统不会真正的生成泛型类,所以instanceof后面不能接泛型类
3 类型通配符
3.1 使用类型通配符
带类型通配符的泛型类,仅仅表示他是所有泛型类的父类,并不能真的把元素添加到起其中;
public class test3
{
//? 通配符,表明可以接受所有的List的泛型
public static void deal(List<?> list)
{
for(Object object : list)
{
System.out.println(object);
}
}
public static void main(String[] args)
{
List<String> list=new ArrayList<>();
list.add("a");
list.add("a");
list.add("a");
list.add("a");
deal(list);
}
}
3.2 设定类型通配符的上限
List
public class Test
{
public static void drawAll(List<? extends Shape> list){
for(Shape shape : list)
{
shape.draw();
}
}
public static void main(String[] args)
{
List<Circle> list=new ArrayList<>();
list.add(new Circle());
list.add(new Circle());
list.add(new Circle());
drawAll(list);
}
}
3.3 设定类型形参的上限
public class Apple{..} 表示此Apple泛型类只能传入Number类及其子类作为类型形参;
4 泛型方法
泛型方法:就是在声明方法的时候定义一个或者多个类型参数;
并且泛型方法无需显示的声明类型参数;
package generic;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
//泛型方法
public class GenericMethod {
/*说明:这个方法是把数组的元素全部转存到集合中,但是数组中元素的类型不确定,所以定义集合泛型时使用了通配符,
* 但是前面我们已经说过,泛型声明中如果有通配符?,则在这个声明指向的泛型对象中,除了null不能加入任何的对象。
* 所以c.add(o); 会报编译错误。
* 我们使用泛型方法来解决这个问题
**/
public static void fromArrayToCollection(Object[] a, Collection<?> c) {
for (Object o : a) {
//c.add(o); // 编译期错误:he method add(capture#1-of ?) in the type
// Collection<capture#1-of ?> is not applicable for the
// arguments (Object):
}
}
/*
* 泛型函数允许类型参数被用来表示方法的一个或多个参数之间的依赖关系,或者参数与其返回值的依赖关系。
* 如果没有这样的依赖关系,不应该使用泛型方法。
* **/
public static <T> void fromArrayToCollectionWithGeneric(T[] a, Collection<T> c){
for (T o : a) {
c.add(o); // correct
}
}
}
4.1 定义泛型方法
public class GenericMethodTest
{
public static <T> void addAll(T[] a, Collection<T> c)
{
for(T t : a)
{
c.add(t);
}
}
public static void main(String[] args)
{
String[] strs = new String[100];
Collection<Integer> c = new ArrayList<>();
//编译不会通过
addAll(strs, c);
}
}
4.2 泛型方法和类型通配符的区别
大多数时候泛型方法可以和类型通配符进行互换;
但是:如果如果某个方法中的一个参数类型或者返回值类型依赖了另一个形参,这个时候就不可以使用通配符,因为通配符的形参无法确定,这个时候
就需要在方法签名中声明类型形参–即泛型方法;
4.3 java7的菱形语法和泛型构造器
public class Foo<E>
{
public <T> Foo(T t)
{
}
public static void main(String[] args)
{
Foo<Integer> foo=new <String>Foo<Integer>("s");
}
}
4.4 设置通配符下限
在泛型方法中,设置通配符下线的形参可以进行添加操作,因为类型肯定是指定类型的父类或者本身;
没有设置通配符下限的代码
public class Test
{
public static <T> T getData(Collection<T> dest, Collection<? extends T> src)
{
T t = null;
for(T ele : src)
{
t=ele;
dest.add(ele);
}
return t;
}
public static void main(String[] args)
{
Collection<Number> dest=new ArrayList<>();
Collection<Integer> src=new ArrayList<>();
Integer i= getData(dest, src);
}
}
设置通配符下限的代码
public class Test
{
public static <T> T getData(Collection<? super T> dest, Collection<T> src)
{
T t = null;
for(T ele : src)
{
t = ele;
dest.add(ele);
}
return t;
}
public static void main(String[] args)
{
Collection<Number> dest = new ArrayList<>();
Collection<Integer> src = new ArrayList<>();
Integer i = getData(dest, src);
}
}
4.5 泛型方法和方法重载
因为java既允许包含通配符上限,也允许包含通配符下限,所以可以允许在一个类中同时出现两个重载的类,但是在调用的时候,会发生编译报错;
5 擦除和转换
当把一个具有泛型信息的变量赋值给没有泛型信息的变量后,在尖括号里面的类型信息将会被扔掉;
List<Integer> list =new ArrayList<>();
list.add(4);
list.add(5);
List list2=list;
list2.add("ss");
String str=(String) list2.get(0);
6 泛型与数组
java允许创建无上限的通配符泛型数组,例如List
7 注意
1
包含泛型声明的类型可以在定义变量、创建对象的时候传入一个类型实参,从而可以动态的生成无数个逻辑上的子类,但这种子类在物理上并不存在;
2
创建带泛型的自定义类,为该类定义构造器的时候,构造器名还是原来的类名,不要增加泛型声明;
3
如果A 是B 的子类,而G是具有泛型声明的类或者接口,那么G 并不是G的子类。这个千万要注意;
但数组就不同了,如果A是B 的子类,那么A【】数组也是B【】数组的子类;这个就会导致隐藏的风险;
4
不要让泛型方法在识别类型时发生迷惑:
public static <T> void addAll(Collection<? extends T> from, Collection<T> to)
{
for(T t : from)
{
to.add(t);
}
}
5
如果显式的指定了泛型构造器的类型参数,那么就不能再使用“菱形”语法;