泛型
含义:集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象。所以在jdk1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此,此时把元素类型设计成一个参数,这个类型参数叫做泛型。
泛型形式:<类型>
泛型的优缺点
- 不使用泛型的缺点:一般我们在使用的时候,基本往集合中存入的都是相同类型的数据,便于管理,所以,如果什么引用数据类型都可以存入集合就不方便
- 加入泛型的优点:编译时期就可以对类型进行检查,不是泛型那么对应的类型就不可以加入该集合
集合中使用泛型案例
public class Tset6 {
public static void main(String[] args) {
Object o = new Object();
String s = new String();
o=s;//多态的一种形式
Object[] objects = new Object[10];
String[] strings = new String[10];
objects=strings;//多态的一种形式
ArrayList<Object> l1 = new ArrayList<>();
ArrayList<String> l2 = new ArrayList<>();
//l1=l2;报错
//因为l1,l2底层都是Object类型的数组,而这个泛型仅仅是对我们写代码时进行限制,所以l1和l2本质上完全没继承关系,全是Object类型
}
}
泛型总结
- 泛型就相当于一个标签,其是jdk1.5以后推出的
- 泛型实际上就是一个<>引起来的参数类型,这个参数类型只有在确定的时候才会确定具体类型
- 使用泛型以后可以确定集合中存放的数据类型,并且数据类型是否一致在编译期就会检查出来
- 泛型的类型都是引用数据类型,不能是基本数据类型
- 使用泛型之后,后续的遍历等操作简单
- 在jdk1.7以后ArrayList<Integer> integers = new ArrayList<>()(new ArrayList<>())里面的<>(钻石运算符)可以不用写类型了,因为后面的<>会根据前面的泛型进行类型推断
- A和B是子类父类关系,但G<A>和G<B>不存在继承关系,其是并列关系,因为泛型的本质就是限定,G<A>和G<B>底层都是Object数组
自定义泛型结构
- 泛型类
- 泛型接口
- 泛型方法
泛型类
//普通类
public class GenericTest {}
//泛型类
public class GenericTest<A> {}
泛型类案例
普通情况
//泛型类
public class GenericTest<E> {
int age;
String name;
E sex;
public void method1(E n){
System.out.println(n);
}
public void method2(E[] m){
System.out.println(Arrays.toString(m));
}
}
class Test{
public static void main(String[] args) {
//实例化时不指定泛型默认为Object
GenericTest gt1 = new GenericTest();
gt1.method1("hello");
gt1.method1(17);
gt1.method2(new String[]{"a","b","c"});
gt1.method2(new Integer[]{1,2,3});
//实例化的时候指定泛型
GenericTest<String> gt2 = new GenericTest<>();
gt2.method1("必须是string了,我失去了自由");
gt2.sex="男";//因为上面已经指定了泛型为string,因此sex为string类型
}
}
继承情况
父类指定泛型,子类可以直接使用父类方法属性等
//泛型类
public class GenericTest<E> {
int age;
String name;
E sex;
public void method1(E n){
System.out.println(n);
}
public void method2(E[] m){
System.out.println(Arrays.toString(m));
}
}
class SubGenericTest extends GenericTest<Integer>{}
class Test{
public static void main(String[] args) {
//父类指定泛型,子类可以直接使用
SubGenericTest sgt1 = new SubGenericTest();
sgt1.method1(12);//这里父类指定泛型了,因此必须传Integer类型
}
}
父类不指定泛型,那么子类直接变成一个泛型类,这个泛型类型可以在子类创建对象时确定
public class GenericTest<E> {
int age;
String name;
E sex;
public void method1(E n){
System.out.println(n);
}
public void method2(E[] m){
System.out.println(Arrays.toString(m));
}
}
class SubGenericTest<E> extends GenericTest<E>{}
class Test{
public static void main(String[] args) {
SubGenericTest<String> sgt1 = new SubGenericTest();
sgt1.method1("hello");
}
}
泛型类可以定义多个参数类型
public class GenericTest1<A,B,C> {
A age;
B name;
C sex;
public void method(A m,B n,C x){
System.out.println(m);
System.out.println(n);
System.out.println(x);
}
//泛型类的构造器不写泛型类型
public GenericTest1(){}
}
泛型类总结
- <>里面就是一个参数类型,这个类型现在是不确定的,相当于一个占位符,但是现在确定它是一个引用数据类型
- 实例化的时候才会确定泛型类型
- 遇到继承情况,父类为泛型类,若父类指定了泛型,那么子类可以直接使用
- 父类不指定泛型,那么子类直接变成一个泛型类,这个泛型类型可以在子类创建对象时确定
- 若在实例化的时候,不明确指定泛型,那么默认此泛型为Object类型
- 泛型类可以定义多个参数类型
- 泛型类型的构造器不加<>,就按常规写法即可
- 不同泛型的引用类型不可以相互赋值
- 泛型如果不指定,那么就会被擦除,对应类型为Object类型
- 泛型类里的静态方法不能使用类的泛型(因为static方法优先于对象存在,而泛型是创建对象时指定的,怎么用)
- 创建泛型数组:A[] i=(A[])new Object[10]
泛型方法
public class GenericTest2<E> {
//不是泛型方法
public void method1(E e){}
//是泛型方法
public <T> void method2(T t){}
public <T,U> void method3(T t,U u){}
}
泛型方法的要求:这个方法的泛型的参数类型要和当前类的泛型无关。
注意:泛型方法定义的时候需要在返回值类型前面加上<T>,若不加的话会把T当作一种数据类型,然而代码中并没有T类型,便会报错。
泛型方法案例
public class GenericTest2<E> {
//不是泛型方法
public void method1(E e){}
//是泛型方法
public <T> void method2(T t){}
public static <T,U> void method3(T t,U u){}
}
class Test5{
public static void main(String[] args) {
GenericTest2<Object> gt2 = new GenericTest2<>();
gt2.method2("hello");//调用方法时确定泛型方法为字符串类型
gt2.method2(12);//调用方法时确定泛型方法为Integer类型
gt2.method3("hello", true);//调用方法时分别确定了2个泛型
}
}
泛型方法总结
- 不是带有泛型的方法就是泛型方法
- 泛型方法的泛型类型是在方法调用时确定的
- 泛型方法的泛型类型可以是多个
- 泛型方法可以是静态方法
通配符
泛型通配符写法:<?>
经典案例
public class Test7 {
public static void main(String[] args) {
ArrayList<Object> objects = new ArrayList<>();
ArrayList<String> strings = new ArrayList<>();
ArrayList<Integer> integers = new ArrayList<>();
List<?> list=null;
list=objects;
list=strings;
list=integers;
}
}
通配符类型变量操作
public void method3(List<?> list){
//遍历
for (Object a:list){
System.out.println(a);
}
//数据的写入操作
//list.add("abc");报错,因为什么类型都可能接收到,接收到的类型若不是String类型的泛型就没意义了
list.add(null);//可行,但没意义
//数据的读取操作
Object o = list.get(0);
System.out.println(o);
}
通配符总结
- A和B是子类父类的关系,G<A>和G<B>不存在子类父类的关系,其是并列关系,但加入通配符?后,G<?>就变成了G<A>和G<B>的父类
- 如果内部遍历该通配符类型的变量,那么用Object即可
- 在List<?>类型变量作为参数的方法中,不能用list随意添加数据
- 在List<?>类型变量作为参数的方法中,数据的读取操作用Object类型接收
泛型受限
public class Person extends Object{}
class Student extends Person{}
class Test8{
public static void main(String[] args) {
//下面3个集合不具备子类父类的关系
ArrayList<Object> objects = new ArrayList<>();
ArrayList<Person> persons = new ArrayList<>();
ArrayList<Student> students = new ArrayList<>();
//泛型的上限
//List<? extends Person>是List<Person>的父类,也是List<Person的子类>的父类
List<? extends Person> list1=null;
//list1=objects;报错
list1=persons;
list1=students;
//泛型的下限
//List<? super Person>是List<Person>的父类,也是List<Person的父类>的父类
List<? super Person> list2=null;
list2=objects;
list2=persons;
//list2=students;报错
}
}