1、泛型
- 加入泛型的优点:在编译时期就会对类型进行检查,不是泛型对应的类型就不可以添加入这个集合,1)提高代码的重用性;2)防止类型转换异常,提高代码的安全性。
- 泛型是JDK1.5以后出现,泛型可以创建变量,但不能实例化
- 泛型实际就是一个<>引起来的参数类型,这个参数类型 ,具体在使用的时候才会确定具体的类型
- 使用泛型之后,可以确定集合中存放数据的类型,在编译时期就可以检查出来
- 使用泛型可能觉得麻烦,实际使用了泛型才会简单,后续的遍历等操作简单
- 泛型的类型:都是引用数据类型,不能是基本数据类型
- 在JDK1.7以后,后面的<>里类型可以不写,通过类型推断出类型的。<>—>砖石运算符
1.1 泛型类/泛型接口
- E要确定,需对当前类实例化才行
- <>里面就是一个参数类型,这个类型暂时不确定,相当于一个占位
- 一定是一个引用数据类型,而不是基本数据类型
- 指定父类泛型,那么子类就不需要再指定泛型了,可以直接使用。
- 实例化的时候不指定泛型;如果实例化的时候不明确的指定类的泛型,那么认为此泛型为Object类型
/**
* GenericTest:普通的类
* GenericTest<E>:泛型类;泛型接口与泛型类类似
* <>里面就是一个参数类型,这个类型暂时不确定,相当于一个占位
* 但是现在确定的是这个类型一定是一个引用数据类型,而不是基本数据类型
* @param <E>
*/
public class GenericTest<E> {
int age;
String name;
E sex;
public void a(E n){
}
public void b(E[] m){
}
}
/**
* 继承情况:
* 父类指定泛型
*/
class SubGenericTest extends GenericTest<Integer>{
}
class Demo{
public static void main(String[] args) {
//指定父类泛型,那么子类就不需要再指定泛型了,可以直接使用。
SubGenericTest sgt = new SubGenericTest();
sgt.a(19);
}
}
/**
* 继承情况:
* 父类不指定泛型,那么子类也会变成一个泛型类,那这个E的类型可以在创建子类对象的时候确定
*/
class SubGenericTest2<E> extends GenericTest<E>{
}
class Demo2{
public static void main(String[] args) {
SubGenericTest2<String> sgt2 = new SubGenericTest2<>();
sgt2.a("abc");
sgt2.sex = "女";
}
}
class Test{
public static void main(String[] args) {
//GenericTest进行实例化
//实例化的时候不指定泛型;如果实例化的时候不明确的指定类的泛型,那么认为此泛型为Object类型
GenericTest gt1 = new GenericTest();
gt1.a("abc");
gt1.a(17);
gt1.a(9.8);
gt1.b(new String[]{"a","b","c"});
//实例化的时候指定泛型,推荐使用泛型
GenericTest<String> gt2 = new GenericTest<>();
gt2.sex = "男";
gt2.a("abc");
gt2.b(new String[]{"a","b","c"});
}
}
- 泛型参数存在继承关系的情况
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
Object obj = new Object();
String s = new String();
obj = s; //父类引用指向子类,多态的一种形式
Object[] objArr = new Object[10];
String[] strArr = new String[10];
objArr = strArr; //多态的一种形式
List<Object> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>(); //泛型是在写代码或编译阶段,进行限制的
/**
* 总结:A和B是子类父类的关系,但是G(A)和G(B)不存在继承关系的。是并列关系
*/
list1 = list2; //此处报错了,因为它们是并列关系
}
}
- 泛型类可以定义多个参数类型
/**
* 泛型类可定义多个参数类型
* @param <A>
* @param <B>
* @param <C>
*/
public class TestGeneric<A,B,C> {
A age;
B Name;
C sex;
public void a(A m,B n,C x){
}
}
- 构造器是不能定义泛型的
- 不同的泛型的引用类型不可以相互赋值
- 泛型如果不指定,就会被擦除,对应的类型就会变为Object类型
- 泛型类的静态方法不能使用类的泛型
- static方法的特点:优先于对象存在,在没有创建对象的时候,方法就被加载到内存了,而参数类型只能在创建对象的时候才能确定,因此静态方法中参数类型和类的参数类型是不同步的
- static方法的特点:优先于对象存在,在没有创建对象的时候,方法就被加载到内存了,而参数类型只能在创建对象的时候才能确定,因此静态方法中参数类型和类的参数类型是不同步的
- 不能直接使用E[]的创建
1.2 泛型方法
- 泛型方法:这个方法的泛型的参数类型要和当前的类的泛型无关
- 泛型方法定义的时候,前面要加上,原因:如果不加的话,会把T当做一种数据类型,然后代码中没有T类型,那么会报错
- T的类型是在调用方法的时候才能确定
- 泛型方法可以是静态方法,静态方法加载到内存,不影响T类型的确定,因为T的类型是在调用的时候才确定
/**
* 泛型方法:这个方法的泛型的参数类型要和当前的类的泛型无关
* @param <E>
*/
public class TestGeneric<E> {
//不是泛型方法,E要确定,需对当前类实例化才行
public void a(E e){
}
//泛型方法,T的类型是在调用方法的时候才能确定
public <T> void b(T t){}
//泛型方法可以是静态方法,静态方法加载到内存,不影响T类型的确定,因为T的类型是在调用的时候才确定
public static <T> void c(T t){}
}
class Demo{
public static void main(String[] args) {
TestGeneric<String> tg = new TestGeneric<>();
tg.a("abc");
tg.b("abc");
tg.b(19);
}
}
1.3 自定义泛型结构–通配符?
- 在没有通配符的时候,下面的a方法,相当于方法的重复定义,报错
- 引入通配符
- A和B是子类父类的关系,G<A>和G<B>不存在子类父类关系,是并列的,加入通配符?后,G<?>就变成了G<A>和G<B>的父类
import java.util.ArrayList;
import java.util.List;
public class Test {
public void a(List<?> list){
//内部遍历的时候用Object即可,而不能用?
for (Object o : list) {
System.out.println(o);
}
}
}
class T{
public static void main(String[] args) {
Test t = new Test();
t.a(new ArrayList<Integer>());
t.a(new ArrayList<String>());
t.a(new ArrayList<Object>());
}
}