1、我们为什么要使用泛型?
早期的Object类型可以接收任意的对象类型,但是在实际的使用中,
会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这
个安全问题。
2、泛型包括:泛型类、泛型方法、泛型接口
3、泛型是什么?
为类中数据传入一个类型参数,并且必须是类类型,不可以是基本类型,可以传入多个,但是若没有传入,则默认是Object类型。
泛型实质:是将数据类型参数化
最简单的一个泛型类:
class fanxing <T,A>{//泛型类
T data;
public T getData() {//泛型方法
return data;
}
}
从上面的例子可以看出,其中的data前的类型成为了参数,这就是泛型的利用,规定类型的使用。
4、创建泛型类的对象
class fanxing <T,A>{//泛型类
T data;
public T getData() {//泛型方法
return data;
}
}
public class fanxingDome{
public static void main(String[] args) {
fanxing <String,Integer> a=new fanxing<>();//创建泛型类对象
System.out.println(a.getData());//调用泛型类中的方法
}
总之:要在< >中写明指定参数,不写则默认是Object(编译期间)
4、从泛型类派生子类
对子类的要求:
(1)将子类也定义成泛型类,并且格式和父类相同,如下:
class child <T> extends parent<T>{
}
// 并且当子类定义类型后,父类也会随之明确
(2)子类并没有定义成泛型类,但是子类必须明确父类的泛型
class child extends parent<String> {
}
注意:泛型接口的子类与泛型类的子类基本一致,不再列举。
5、泛型通配符
一般是使用“ ?”来代替具体类型实参,而不是形参(多用于方法参数列表中)
class Demo1<T>{ }
public class fanxingtongpeifu {
static void test(Demo1<?> d){}//在此方法中,本应该是传来一个具体参数的Demo1对象,但是因为Demo1是一个泛型类,还未确定其具体参数类型,所以用?代替。
public static void main(String[] args) {
Demo1<Number> a=new Demo1<>();
Demo1<Integer> b=new Demo1<>();
}
}
static void test(Demo1<?> d){} //在此方法中,本应该是传来一个具体参数的Demo1对象,但是因为Demo1是一个泛型类,还未确定其具体参数类型,所以用“ ?”来代替。
除此之外,我们还可以用泛型通配符来限制传入的泛型的类型:
以上面的test()方法为例
static void test(Demo1<?> d){
Demo1<?> as=new Demo1();
}
static void test(Demo<? extends Number> d){} 设置上限为Number,只能传入number及其子类
static void test(Demo<? Super Number> d){} 设置下限为Number,只能传入number及其父类
<? extends 某类型> 来设置上限
<? super 某类型> 来设置下限
即就是我们方法的实参在有用到泛型类时,需要用到 ?来表示泛型参数,并且我们可以对其设置一定的范围来限制。
6、类型擦除
泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是,泛型代码能够很
好地和之前版本的代码兼容。那是因为,泛到信息只存在于代码编译阶段,在进入JVM之
前,与泛型相关的信息会被擦除掉,我们称之为一类型擦除。
即就是,我们所使用的泛型规定,只是规定了代码编译期间的类型
,实际上在底层,都是Object类型在进行操作。泛型只是将表层限制,顶层内部结构还是Object类。