Java | 泛型
泛型
什么是泛型?
泛型,即‘参数化类型’,顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
为什么要使用泛型?
先看一个例子:
List list = new ArrayList();
list.add("abc");
list.add(1);
for (Object o : list) {
String str = (String)o;
System.out.println(str.length());
}
在这个小Demo运行的结果:java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
现以这样的结果不是我们想要的,但是如何避免这样的错误呢?
所以这个时候,就要使用泛型了:
List<String> list = new ArrayList<>();
list.add("abc");
//list.add(1); //报错,不能添加非字符串类型
list.add("efg");
我们在声明的时候,加上泛型,是一个字符串类型,所以如果使用这个集合添加别的类型的值,他就会报错,并且还是在编译阶段就报错。
所以使用泛型可以有效有避免在编译中所遇到的错误,可以快速的发现错误并修改。
泛型的特性
下面有一个例子,在家猜一下答案:
List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();
System.out.println(stringList.equals(integerList));
上面这一段代码中,定义了两个集合,然后集合中都没有存储数据,但是他们的泛型是不一样的,最后比较一下,他们的结果:true
。
这就是泛型的特性,泛型的擦除
,泛型只在译编阶段有效果,在编译的时候,只要编译正确就会将泛型的相关信息给擦除了,所以说泛型的信息不会进入到运行阶段。
当然,有的时候,定义泛型之后,还需要存储别的类型,这个时候,可以使用反射来往容器里面存数据。
泛型的分类
泛型分为三种:泛型类
,泛型接口
,泛型方法
。
泛型常见的字母:
T Type 表示类型。
K 代表键值对中的Key
V 代表键值对中的Value
E 代表Element
?表示不确定类型
泛型类
定义一个泛型类:
public class Student<T> {
T id;
public T getId(T id) {
return id;
}
public void setId(T id) {
this.id = id;
}
}
使用:
Student<Integer> stu = new Student<>();
stu.setId(1);
Integer id = stu.getId();
System.out.println(id); //1
Student<String> stuStr = new Student<>();
stuStr.setId("10001");
String strid = stuStr.getId();
System.out.println(strid); //10001
Student stuStr = new Student);
stuStr.setId("2222");
Object objid = stuStr.getId();
System.out.println(objid); //2222
在上面的例子中,我们可以看到,使用不同的泛型声明对象,存进去和取出来的id类型是不是一样的,并且,如果没有定义泛型,默认的就是Object类型。就是一种泛型的擦除。如果定义的话,那么所有的Student
类里面的<T>
都会替换成声明进修的类型。
泛型类的继承
在泛型的继承里面,重写的方法都是由父类的泛型所决定的,如果父类没有决定泛型,那么子类也不可以指定泛型,如果父类和子类都没有指定泛型,那么<T>
就会由Object
来代替。在子类里面定义属性的时候,是根据子类的泛型的。
父类指定泛型
class Test extends Student<String>{
@Override
public String getId() {
return super.getId();
}
@Override
public void setId(String id) {
super.setId(id);
}
}
父类子类都没有泛型
class Test1 extends Student{
@Override
public Object getId() {
return super.getId();
}
@Override
public void setId(Object id) {
super.setId(id);
}
}
父类子类都有泛型
class Test2<T> extends Student<T> {
@Override
public T getId() {
return super.getId();
}
@Override
public void setId(T id) {
super.setId(id);
}
}
只有子类有泛型
class Test2<T> extends Student {
@Override
public Object getId() {
return super.getId();
}
@Override
public void setId(Object id) {
super.setId(id);
}
}
泛型接口
泛型的接口使用的时候,和泛型的类的定义是一个的,不管现实还是继承都和泛型类一样。
public interface GenericInterface<T> {}
泛型方法
泛型方法的格式:
修饰符 <代表泛型的变量> 返回值类型 方法名(参数列表){}
含有泛型的方法,在调用方法的时候确定泛型的数据类型,传递什么类型的参数,泛型就是什么类型的。
定义一个泛型方法并使用:
public static void main(String[] args) {
Demo01 demo01 = new Demo01();
demo01.test("a"); //a
demo01.test(2); //2
demo01.test(false); //false
demo01.test(0.01); //0.01
Demo01.staticTest(4); ///4
}
/*
普通泛型方法
*/
public <T> void test(T t) {
System.out.println(t);
}
/*
静态泛型方法
*/
public static <T> void staticTest(T t) {
System.out.println(t);
}
泛型通配符
当我们不知道要使用的具体类型的时候,我们就可以使用<?>
来表示泛型。
使用:
public static void main(String[] args) {
//可以声明的时候使用,但是不可以定义的时候使用
List<?> list = new ArrayList<String>();
//定义的时候,必须要指定具体的类型
//List<?> list1 = new ArrayList<?>();
List<String> strings = new ArrayList<>();
test(strings);
//<?>通配符可以接收任何类型
List<Integer> integerList = new ArrayList<>();
test(integerList);
}
public static void test(List<?> list) {}
在泛型里面不存在继承的关系,如:
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
//test(strings); //报错
}
public static void test(List<Object> list) {}
不可以用Object类型的集合来接收String类型的集合,但是有的时候,我们必须要用这一种方法,那么我们就可以给泛型设置上限和下限来完成这种操作。
泛型的上限
格式: 类型名称 <? extends 类 > 对象名称
意义: 只能接收该类型及其子类
泛型的下限
格式: 类型名称 <? super 类 > 对象名称
意义: 只能接收该类型及其父类型
比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类。
public static void main(String[] args) {
Collection<Integer> list1 = new ArrayList<Integer>();
Collection<String> list2 = new ArrayList<String>();
Collection<Number> list3 = new ArrayList<Number>();
Collection<Object> list4 = new ArrayList<Object>();
getElement(list1);
getElement(list2);//报错
getElement(list3);
getElement(list4);//报错
getElement2(list1);//报错
getElement2(list2);//报错
getElement2(list3);
getElement2(list4);
}
// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
public static void getElement1(Collection<? extends Number> coll){}
// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
public static void getElement2(Collection<? super Number> coll){}
关注公众号,随时获取最新资讯
细节决定成败!
个人愚见,如有不对,恳请斧正!