基础
- 泛型值
- 泛型值只能为类对象,不可以是基本类型,需要使用基本类型的包装类型
- 当泛型参数未指定时,默认为泛型类型为object类型,如
ArrayList li=new ArrayList();
li.add("ni");
li.add(2);
-
继承关系
- 泛型之间不可以继承,如Integer和Number是继承关系, Box和Box没有关系
- 存在类似继承关系时,使用通配符
-
泛型擦除
- 泛型信息不会进入到运行时阶段。
- 泛型仅在编译前有效,编辑阶段阶段检查泛型正确后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法
-
泛型类
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
- 泛型接口
- 同类
//定义一个泛型接口
public interface Generator<T> {
public T next();
}
/**
- 传入泛型实参时:
- 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T>
- 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。
- 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
- 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。
*/
public class FruitGenerator implements Generator<String> {
private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
@Override
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
- 泛型方法
- 泛型方法,是在调用方法的时候指明泛型的具体类型 ,而非调用泛型类中含有泛型的方法
- 静态方法使用泛型时,必须使用方法泛型。
- 静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
- 对于泛型方法,会自动从参数中推测泛型类型,当参数中未使用时,则未object类型,因为此时只能返回object类型的参数
- 泛型方法的返回值会从参数中推测,返回值的接收对象中推测,当无法判断时,默认为object;
//如下
//与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
public [static] <T> T func(){}
//例子
public <T> void func(List<T> list, T t) {
list.add(t);
}
-
通配符
- 在方法形参为一个泛型对象时;若希望可以传入任意类型的泛型对象,可以在形参声明中不明确指定泛型类型,而是使用?
- 不可以使用object代替?;泛型不存在继承关系,如
public static void cc(List<Object> v) { }
,则方法只能传入List<Object>
类型的参数,List<Integer>
不可以作为该方法的参数
-
通配符上下限
- ?super integer 定义了下限,如
list<?super integer >
形式- 存值时,只能存储integer的子类,
- 取值时,返回值object类型,其他类型需要强行转化
- ? extends E为上限,如list<? extends number>形式
- 不可以添加值,
- 要求对象为number的子类;
- 若对象为E 子类但子类之间无法进行类型转化;不可行
- 若存储父类,因为父类无法自动转化为子类,同样不可以;
- 若存储E,实际对象可能为E的子类,同样不可以;
- 取值时,返回值为number类型
- 不可以添加值,
- ?super integer 存储类型详解
- ?super integer 代表数据为integer的父类;
- 若interger 存在多个父类,如A和B;此时可以构造两种类型的对象,如下;此时,存储值时,若对象为A类型,则无法存入B类型数组中;若对象为integer或者integer的子类,则可以直接存入;
- java 子类可以直接转化为父类;
- ?super integer 定义了下限,如
// ?super integer 介绍 ;
List<? super Integer> li=new ArrayList<A>();
List<? super Integer> li=new ArrayList<B>();
4.
4.
5. 如number 等,会存在和number同层次的类B,此时 ,无法确定数据具体类型,且number类和B类无法相互转换;故只能存储Integer类及其子类;这些类可以被转化为统一的父类类型;
5.
6. - 对于形参为? extends E 或 super,e类型都可以传入
7. 准则:存储数据时,不会破坏数据
8. java 存储值时,要求数据完整性,如integer和short 类型不可以转换为number存储,会对数据产生影响,故无法判断具体类型时,会要求其不可添加或者取值
9.
//通配符上下限 声明规则
List<? extends Number> foo3 = new ArrayList<Number>; // Number "extends" Number
List<? extends Number> foo3 = new ArrayList<Integer>; // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>; // Double extends Number
//存取值
public static void cc(List<? extends Number> v) {
//可以取值
Number number = v.get(0);
//报错
~~v.add(number);wrong~~
}
使用
- 类型判断
- 不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错
if(ex_num instanceof Generic<Number>){ }
- 从方法入参中判断参数类型
- 接收对象中的泛型类型要与参数中相同,不然会报错;
public static <T> List<T> CC( T c) {
return new ArrayList<>();
}
//如
List<Integer> cc = CC("c");报错
- 从方法返回值中判断参数类型
- 不可行,返回类型中要不使用泛型T,反否则返回的类型中泛型必须时T,或者不写
public static <T> List<T> CC() {
//报错
return new ArrayList<String>();不可行,可以不写 默认object
- 从方法返回值的接收对象中判断
//调用方法时 返回list<object>
public <T> List<T> CC() {
return new ArrayList<>();
}
// 可以通过指定返回方法接收对象的类型来改变泛型
List<Integer> cc = CC();