😀😀😀创作不易,各位看官点赞收藏.
Java之泛型
泛型:通俗来讲就是一个标签,集合在设计时不能确定集合存放元素的类型。JDK5之后引入了泛型,在定义类、接口时,通过一个标识标识类中某个属性、方法的返回值和参数类型。把元素类型设计成一个参数,例如Collection、List、Map<E,V>等,E和V就是泛型集合。使用泛型以后集合中就只能存放泛型对应的类型元素。
泛型的好处:
- 解决元素存储的安全问题,这样在集合中就能确定集合存放元素类型。
- 解决获取集合元素强制转换问题,使用了泛型就不需要再进行类型的强制装换。
1、使用泛型
public static void main(String[] args) {
// 将list声明为泛型,这个集合中只能存放对应泛型类型的元素,即只能存放String类型
ArrayList<String> list = new ArrayList<>();
list.add("你好");
list.add("Hello");
list.add("world");
// 遍历集合,list的迭代器自动添加了泛型
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
// 取出的元素也是之前设置的类型
String next = iterator.next();
System.out.println(next);
}
}
注意:
- 在
JDK5
之后,集合和集合类中都修改成了泛型结构,在实例化集合类时就可以指明具体的泛型类型。 - 在使用时,集合类或接口中内部使用到了泛型,这个泛型就是在实例化集合或类时指定的类型。
- 泛型类必须是类,不能是基本数据类型,对于基本数据类型需要使用它的包装类。
- 如果实例化时没有使用泛型,默认的泛型就是
java.lang.Object
。
2、自定义泛型类
// 自定义泛型类,T就是定义的泛型
public class Student<T> {
String name;
int gae;
// 类中使用自定义泛型
T info;
public Student() {}
public Student(String name, int gae, T info) {
this.name = name;
this.gae = gae;
this.info = info;
}
public T getInfo() {
return info;
}
public void setInfo(T info) {
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", gae=" + gae +
", info=" + info +
'}';
}
}
public static void main(String[] args) {
// 实例化一个自定义泛型类,将Student内部的泛型指定为String类型
Student<String> student = new Student<>("张三",18,"我是张三");
System.out.println(student);
}
注意事项:
- 泛型可能存在多个,存在多个泛型是每一个泛型使用逗号隔开。
public class Student<T1,T2> {}
- 泛型类中的构造器不能带有泛型。
- 泛型不同之间的两个同一个类的实例对象不能相互赋值,即使泛型参数存在子父类关系。
Student<String> student1 = new Student<>();
Student<Integer> student2 = new Student<>();
student1 = student2; // 两个对象的泛型不一致,导致不能相互赋值
// 这是在编译期时报错,但是JVM加载的时候还是同一个Student对象
- 实例对象时,如果不指定泛型类型默认按照Object类型处理,但是不等同于Object,可能存在类型转换异常。
- 泛型类型不能是基本数据类型,对于基本数据类需要使用包装类。
- 泛型内部的静态方法不能使用泛型,因为静态方法是类加载有的,但是泛型是实例化对象时才指明的。
- 自定义异常类不能是泛型类。
- 创建泛型数组时不能直接 new 泛型数组,而是new Object[],然后强制转换成泛型数组。
// T[] array = new T[10]; 因为T始终是一个变量
T[] array = (T[]) new Object[10];
自定义子类泛型:
class SubClass extends SuperClass<Integer> {} // 继承父类指明的泛型,抹除了泛型
class SubClass<T> extends SuperClass<T> {} // 继承父类中的泛型
class SubClass<T1> extends SuperClass<Integer,T1> {} // 继承父类中部分的泛型
class SubClass<T1,A> extends SuperClass<Integer,T1> {} // 继承父类中的部分泛型并且拥有自己的泛型
3、泛型方法
在方法中出现泛型结构但是和类的泛型参数没有任何关系的方法称为泛型方法。
public class Demo02<T> {
T t;
// 非泛型方法:即使使用了类的泛型参数也不是泛型方法
public void test1(){
System.out.println(t);
}
// 泛型方法:public <E>表示这是一个泛型方法,泛型参数为E,返回值、参数、方法中都可以使用这个泛型参数
public <E> List<E> test2(E[] e){
ArrayList<E> list = new ArrayList<>();
for (E e1 : e) {
list.add(e1);
}
return list;
}
}
注意事项:
- 泛型方法和泛型类没有任何关系,拥有泛型方法的类不一定是泛型类。
- 泛型方法可以是静态方法,因为泛型方法在调用时就可以确定泛型参数类型,可以不用实例化对象。
4、泛型通配符
不同泛型参数相互之间不能赋值,但是有时候泛型参数之间可能需要存在子父类关系,我们就使用通配符来表示。泛型中的通配符就是 ?
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
// 使用通配符
ArrayList<?> list3 = null;
list3 = list1; // 可以进行赋值,list3就可以作为list1、list2的通用父类
}
泛型通配操作:
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
// 使用通配符
ArrayList<?> list3 = null;
list3 = list1;
// 使用了泛型通配符后就不能向集合中添加数据了,但是可以添加null值
list3.add(123); // 报错
list3.add(null); // 不报错
// 但是还是可以读取数据,返回值是Object对象数据
for (Object o : list3) {
System.out.println(o);
}
}
有限制条件的通配符:可以限制通配符表示的范围,例如只能是某个类的子类等。
public static void main(String[] args) {
// ?表示的是SuperClass及其子类
List<? extends SuperClass> list1 = null;
// ?表示的是SubClass及其父类
List<? super SuperClass> list2;
List<SubClass> list3 = new ArrayList<>();
List<SuperClass> list4 = new ArrayList<>();
List<Object> list5 = new ArrayList<>();
list1 = list3;
list1 = list4;
// list1 = list5; // 报错
// list2 = list3; // 报错
list2 = list4;
list2 = list5;
// list1不能添加数据,可能表示的是一个能小的子类,然后参数为一个大一点的子类,就会报错
// list2可以添加SuperClass及其子类,因为list2表示的是SuperClass,最小子类就是SuperClass
list2.add(new SubClass());
}