1.我们在声明完自定义泛型类以后,可以在类内部(比如:属性、方法、构造器中)使用类的泛型。
2.我们在创建自定义泛型类的对象时,可以指明泛型参数类型,一旦指明,内部凡是使用类的泛型参数的位置,都要具体化为指定的类的泛型类型。
3.如果在创建自定义泛型类的对象时,没有指名泛型参数类型,那么泛型将被擦除,泛型对应的类型均按照Object处理,但不等价于Objcet。
4.泛型的指定中必须使用引用数据类型。不能使用基本数据类型,此时只能使用保证类替换
5.除创建泛型类对象外,子类继承泛型类是、实现类实现泛型接口时,也可以确定泛型结构中的泛型参数。
public class Sub2 extends Order<Integer>{
}
在给泛型类提供子类时,子类也不确定泛型的类型,则可以使用泛型参数。
public class Sub3<T> extends Order<T>{
}
还可以在现有的父类的泛型参数的基础上,新增泛型参数。
public class Sub5<T,E> extends Order<T>{
}
public class Sub4<E> extends Order<Integer>{
}
注意点
1.泛型类可能用多个参数,此时应将多个参数一起放在简括号内。比如
2.JDK7.0开始,泛型的简化操作:
ArrayList<Fruit> list =new ArryList<>();
3.如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象(本来语法就不行)
4.不能使用 new E[ ]。但是可以:
E[ ] elements=(E[ ])new Object [capacity];
参考:ArrayList源码中声明:Object [ ] elementData,而非泛型参数类型数组。
5.在类/接口上声明的泛型,在本类或本类接口中即代表某种类型,但不可以在静态方法中使用类的泛型。
(不能在静态方法使用!!!)
6.异常类不能是带泛型的。
2.自定义泛型方法
2.1 问题:在泛型类的方法中,使用类类的泛型参数。那么此方法是泛型方法么?不一定
2.2格式
权限修饰符 返回值类型 方法名(参数列表)
2.3举例
public E method(E e){ //通常在形参列表或返回值类型的位置会出现泛型参数T
public <T> ArrayList<T> copyFromArrayList(T[] arr){
ArrayList<T> ts = new ArrayList<T>();
for (int i=0;i< arr.length;i++){
ts.add(arr[i]);
}
return ts;
}
2.4 说明
声明泛型方法时,一定要添加泛型参数
泛型参数在方法调用时,指明其具体需求
泛型方法可以根据需要声明为static的
泛型方法所属的类是不是一个泛型类都可以
package com.genericity;
import org.junit.Test;
import java.util.ArrayList;
/**
* @Author Tom
* @Date 2023/12/26 10:48
* @Description: 测试泛型类
*/
public class GenericTest {
@Test
public void test1() {
//实例化时,就可以指明类的泛型参数的类型
Order order = new Order();
Object obj=order.getT();
//泛型参数在指明时,是不可以使用基本数据类型的!但是可以使用包装类替代基本数据类型。
//Order order1=new Order<>();
//在实例话时,可以指明类的泛型参数的具体类型!一旦指明泛型的类型,则在泛型类中凡是使用泛型
//参数的位置,都替换为指定的类型
Order<Integer> order2=new Order<>();
order2.setT(12);
Integer t = order2.getT();
System.out.println(t);
}
@Test
public void test2() {
//体现继承型的多态性
Object object1=null;
String str1=null;
object1=str1;
Object[] object2=null;
String[] str2=null;
object2 = str2;
}
@Test
public void test3(){
ArrayList<Object> list1 = new ArrayList<Object>();
ArrayList<String> list2 = new ArrayList<String>();
//list1=list2;
// list1=list2; //不可以
/**
* 反证法:
* 假设list1=list2是可以的
* list2.add("AA")
* list1.add(123);
* String str-list2.get(1);//相当于取的123赋值给str。错误的
* 总结不行
*/
}
}
1.SuperA是类A的父类,则G与G的关系:
并列的两个类,没有任何子父类关系,不能以多态互相赋值
@Test
public void test4(){
List <String> list1=null;
ArrayList<String>list2=new ArrayList<>();
list1=list2;
}
2.类SuperA是类A的父类或接口,SuperA 与A 的关系:SuperA 与 A 有继承或实现的关系。
即A 的实例可以赋值给SuperA 类型的引用(或变量)
比如:List 与 ArrayList
public static <E> List<E> exchangeArray(E []arr){
ArrayList<E> es = new ArrayList<E>();
for (int i=0;i< arr.length;i++){
es.add(arr[i]);
}
return es;
}
通配符
2.使用说明
举例:ArrayList>
G>可以看做是G类型的夫类,即可以将G的对象赋值给G>类型的引用(或变量)
3.读写数据的特点(以集合ArrayList>为例说明)
读取数据:允许的,读取的值的类型为Object类型
写入数据:不允许的。特例:写入null值
4.有限条件的通配符
List extends A> :可以将List或List赋值给List extends A>。其中B类是A类的子类
List Super A> :可以将List或List赋值给List Super A>。其中B类是A类的父类
例子
class Father{} 父类略
class Son extends Father{} 子类略
@Test
public void test5(){
List<? extends Father> list=null;
List <Object> list1=null;
List <Father> list2=null;
List <Son> list3=null;
list=list1; //false
list=list2; //true
list=list3; //true
}
@Test // 相当于>=
public void test5(){
List<? extends Father> list=null;
List <Father> list1=null;
list1.add(new Father());
list=list1;
//读取数据可以!!而且数据类明确
Father father = list.get(0);
System.out.println(father);
//写入数据不行!!
list.add(new Father());
}
@Test // 相当于>
public void test6(){
List<? super Father> list=null;
List <Father> list1=null;
list1.add(new Father());
list=list1;
//读取数据可以 但是类型不明确
Object obj = list.get(0);
System.out.println(list.get(0));
//写入数据 可以将 子类或者父类写入进来
list.add(null);
list.add(new Father());
list.add(new Son());
}