泛型
什么是泛型?为什么使用泛型?
泛型的出现意味着编写的代码可以被不同类型的对象所重用,提升了代码的重用性。泛型的本质是参数化类型,即将所需操作的数据类型设置为一个参数。 举个实际中的栗子:我们需要设计一个柜子的类,柜子可以用于存放书本、食品或者衣物,但是我们在设计阶段不能确定柜子里具体要存放什么东西,那怎么来设计这样一个通用型的柜子,这里就可以用到泛型。所以,我们把存放元素的类型设计成一个参数,这个类型参数就称之为泛型。 举个JAVA中的栗子:ArrayList,List,这个就是类型参数,也就是泛型。为什么这么写呢?因为我们在创建Arraylist或者List时,无法确定里面具体存储的元素的类型,但是我们有希望一个实例对象中存储的元素的类型时一致的,这就需要泛型来实现操作。
int[] arr1 = new int[] {159,357,456};//没有用泛型,ArrayList中存储的元素格式各样,实际开发中在操作时是很容易出现问题的List list1 = new ArrayList();list1.add(123);list1.add("敲代码的阿茄");list1.add(arr1);//使用泛型,限制ArrayList存放的元素的类型,就不能添加存储其他类型的元素了List<String> list2 = new ArrayList<>();list2.add("敲");list2.add("代");list2.add("码");list2.add("的");list2.add("阿");list2.add("茄");//list2.add(123);//无法添加//list2.add(arr1);//无法添加
泛型使用的基本要求
- 异常类不能声明为泛型。
- 泛型不能是基本数据类型,需要使用基本数据类型的泛型可以写成其包装类。
//List<int> list2 = new ArrayList<>();List<Integer> list2 = new ArrayList<>();
自定义泛型类
在类或接口中声明的泛型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值。但是,不能在静态方法中使用类的泛型。
// 泛型类:类中定义有不确定的类型public class Dict<T> {String dictName;int dictCount;T dictT;//泛型类下用了泛型的方法,报错,需要去掉static//public static void get(T dictT) {// this.dictT = dictT;//}//泛型类下没用泛型的方法public static void set() {System.out.println("");}}
泛型方法和泛型类下的方法?
泛型方法:该泛型方法所在的类可以不是泛型类。往直观一点讲,方法中出现了泛型结构<>。 那么,泛型方法可以是静态的吗?可以,因为泛型参数是在调用方法时确定的,并非在实例化时确定的。
public class GenericTest {public static void main(String[] args) {Dict<Float> dict = new Dict<>();dict.set1(0.0f);//调用泛型方法,泛型类型跟类的泛型没关系,不受影响dict.set2(" ");dict.set2(36);dict.set2(new ArrayList());}}//泛型类class Dict<T> {String dictName;int dictCount;//泛型类中的方法:如果调用了泛型则不能为静态public void set1(T dictT) {System.out.println("我是泛型类下的方法");}//泛型方法:可以为静态public static <T> void set2(T dictT) {System.out.println("我是泛型方法");}}
泛型与继承
- 如果类A是类B的父类,但是,
G<A>
不是G<B>
的父类(G是类或者接口),两个属于不同的类。所以不存在两者间的多态和向上转型。 - 但是呢,
A<G>
仍然是B<G>
的父类。
通配符
通配符:?
- 利用通配符,创建二者的共同父类,
G<A>
和G<B>
的共同父类G<?>
; - 但是,不能往其中添加新数据,常用于赋值操作而已,仅能添加
null
; - 允许读取数据,读取数据的类型为object;
举例说明:定义List<?>
是List<Object>
和List<String>
的公共父类。
//不能添加数据,除了nullList<?> list = new ArrayList<>();//list.add(16);//报错//list.add(" ");//报错list.add(null);//允许读取数据List<?> list = new ArrayList<>();List<String> list1 = new ArrayList<>();list1.add("newstring");list =list1;list.add(null);Object obj = list.get(0);System.out.println(obj);//newstring
有限制条件的通配符
G<? extends A>
可以作为G<A>
和G<B>
的父类,其中B是A的子类;即,可以作为所有继承于A类的类G<A的子类>
的父类。G<? super A>
可以作为G<A>
和G<B>
的父类,其中B是A的父类;即,可以作为所有A的父类的G<A的父类>
的父类。
// 下面举例类的关系:Earth extends Sun, Sun extends UniverseList<? extends Sun> list1 = new ArrayList<>();// 可以作为List<Sun及其子类>的父类List<? super Sun> list2 = new ArrayList<>();// 可以作为List<Sun及其父类>的父类List<Universe> list3 = new ArrayList<>();List<Sun> list4 = new ArrayList<>();List<Earth> list5 = new ArrayList<>();list1 = list4;// 多态list2 = list4;//多态// 读取数据Sun s1 =list1.get(0);//获取的数据是Sun或者Sun的子类的对象,可以实现多态Earth e1 =(Earth)list1.get(0);//不强转会报错,因为获取的数据可能是Sun的对象,父类转子类需要强转Sun s2=(Sun)list2.get(0);//不强转会报错,获取的数据可能是Sun的父类,父类转子类需要强转Object o=list2.get(0);//不强转情况下,只能是Object// 写入数据list2.add(new Sun());//只能添加Sun本身,或者Sun的子类//因为list2存的可能是Sun及其父类,假设new的是Universe,但是list2中存储的是Sun,//即实际?=Sun,那么Universe作为父类是无法直接赋给子类的list2.add(new Earth());//list1.add();//无法添加数据,因为你无法确定存储的子类有多小