java获得集合的泛型类型_Java集合之泛型类和泛型接口

1. 为什么要有泛型

1.1 不使用泛型的问题

当不使用泛型时,集合中是可以存放任意类型对象的,只要把对象存储集合后,那么他们都会被提升为Object类型。当我们在取出每一个对象,并且进行相应的操作,需要进行类型转换。

观察下面代码:

public static void main(String[] args) {

Collection coll = new ArrayList();

coll.add("abc");

coll.add("def");

coll.add(5);//由于集合没有做任何限定,任何类型都可以存放

Iterator it = coll.iterator();

while(it.hasNext()){

//需要打印每个字符串的长度,就要把迭代出来的对象转成String类型

String str = (String) it.next();

System.out.println(str.length());

}

}

程序在运行时发生了问题 java.lang.ClassCastException。为什么会发生类型转换异常呢?

由于集合没有使用泛型,什么类型的元素都可以存储,导致取出时强转引发运行时 ClassCastException。怎么来解决这个问题呢?

Collection虽然可以存储各种类型对象,但一般只存储同一类型对象,例如都是字符串对象。因此在JDK5之后,新增了泛型(Generic)语法,让你在设计API时可以指定类或方法支持泛型,这样我们使用API的时候也变得更为简洁,并得到了编译时期的语法检查。

1.2 泛型概念

泛型:参数化类型,也就是说要操作的数据一开始不确定是什么类型,在使用的时候,才能确定是什么类型,把要操作的数据类型指定为一个参数,在使用时传入具体的类型,实现代码的模板化。

//比如ArrayList集合(含有泛型),部分源代码如下(可以理解为模板)

public class ArrayList {

public E get(int index) {...}

public E set(int index, E element) {...}

public boolean add(E e) {...}

public E remove(int index) {...}

}

//创建ArrayList集合对象指定类型为String时

List list1 = new ArrayList<>();

//模板代码相当于变成如下形式

public class ArrayList {

public String get(int index) {...}

public String set(int index, String element) {...}

public boolean add(String e) {...}

public String remove(int index) {...}

}

//所以使用list1集合对象,调用以上四个方法时,所有E的位置都变为了String

//创建ArrayList集合对象指定类型为Integer时

List list2 = new ArrayList<>();

//模板代码相当于变成如下形式

public class ArrayList {

public Integer get(int index) {...}

public Integer set(int index, Integer element) {...}

public boolean add(Integer e) {...}

public Integer remove(int index) {...}

}

//所以使用list2集合对象,调用以上四个方法时,所有E的位置都变为了Integer

jdk1.7之后,泛型的简化操作:ArrayListflist = new ArrayList<>(); 可省略右侧中的数据类型。

1.3 使用泛型的好处

将运行时期的ClassCastException,提前到编译时期变成了编译失败

避免了类型强转的麻烦。

import java.util.ArrayList;

import java.util.Collection;

import java.util.Iterator;

public class GenericDemo {

public static void main(String[] args) {

show02();

}

/**

* 创建集合对象,使用泛型

* 好处:

* 1.想使用元素特有的方法,避免了类型转换的麻烦

* 2.把运行时异常,提升到编译时期

* 弊端:

* 一旦确定数据类型,就只能存储该类型数据

*/

private static void show02() {

Collection list = new ArrayList<>();

list.add("abc");

list.add("def");

// list.add(5);//当集合明确类型后,存放类型不一致就会编译报错

// 集合已经明确具体存放的元素类型,那么在使用迭代器的时候,迭代器也同样会知道具体遍历元素类型

Iterator it = list.iterator();

while(it.hasNext()){

String str = it.next();

//当使用Iterator控制元素类型后,就不需要强转了。获取到的元素直接就是String类型

System.out.println(str.length());

}

}

/**

* 创建集合对象,不使用泛型

* 好处:

* 集合的类型默认为Object类型,可以存储任意的对象

* 弊端:

* 不安全,

* 不能使用元素特有的方法

*/

private static void show01() {

ArrayList list = new ArrayList();

list.add("abc");

list.add(1);

//使用迭代器遍历集合

Iterator it = list.iterator();

while(it.hasNext()){

//取出集合中元素,Object类型

Object obj = it.next();

System.out.println(obj);

/*

想使用字符串特有的方法length

不能使用,需要向下转型

Object obj = "abc";多态

*/

String s = (String)obj;

System.out.println(s.length());

}

}

}

2. 泛型类

2.1 定义格式:

//其中类型参数,一般用一个大写字母表示,比如: T,E,K,V

//如果要定义多个泛型,多个类型参数之间用逗号,隔开,例如:public class HashMap {...}

权限修饰符 class 类名 { }

例如,API中的ArrayList集合:

泛型在定义的时候不具体,使用的时候才变得具体。在使用的时候确定泛型的具体数据类型。

public class ArrayList{

public boolean add(E e){ }

public E get(int index){ }

....

}

2.2 使用泛型: 即什么时候确定泛型。

在创建对象的时候指定类型

例如,ArrayList list = new ArrayList<>();

此时,变量E的值就是String类型,那么我们的类型就可以理解为:

class ArrayList{

public boolean add(String e){ }

public String get(int index){ }

...

}

再例如,ArrayList list = new ArrayList<>();

此时,变量E的值就是Integer类型,那么我们的类型就可以理解为:

class ArrayList {

public boolean add(Integer e) { }

public Integer get(int index) { }

...

}

在类、接口上声明的泛型,在本类、本接口中即代表某种数据类型,可以作为非静态成员变量的类型、非静态方法的形参类型、非静态方法的返回值类型、局部变量的类型等,但不能用于静态成员上。

自定义泛型类:

public class MyGenericClass {

// 没有T类型,在这里代表 未知的一种数据类型 未来传递什么就是什么类型

private T t; // 用于成员变量的数据类型

public MyGenericClass() {

}

public MyGenericClass(T t) { // 用于方法的形参的数据类型

this.t = t;

}

public T getT() { // 用于方法的返回值的数据类型

return t;

}

public void setT(T t) {

this.t = t;

}

public void test() { // 用于局部变量的数据类型

T t;

}

}

使用:

public class GenericClassDemo {

public static void main(String[] args) {

// 创建一个泛型为String的类

MyGenericClass gc1 = new MyGenericClass<>();

// 调用setT

gc1.setT("abc");

// 调用getT

String str = gc1.getT();

System.out.println(str);

//创建一个泛型为Integer的类

MyGenericClass gc2 = new MyGenericClass<>();

gc2.setT(123);

Integer num = gc2.getT();

System.out.println(num);

}

}

注意:

类型实参必须是一个类类型,不能用基本数据类型填充,但可以使用包装类。

继承中的使用:

父类:

public class MyGenericFuClass {

public void print(T t) {

System.out.println(t);

}

}

定义子类继承泛型父类的同时指定泛型的类型

public class MyGenericZiClassA extends MyGenericFuClass {

@Override

public void print(Integer num) {

System.out.println(num);

}

}

public static void main(String[] args) {

// MyGenericZiClassA类已经没有泛型了

MyGenericZiClassA gzA = new MyGenericZiClassA();

gzA.print(100);

}

定义子类继承泛型父类,若不指定的父类泛型类型,子类也必须声明为泛型类,创建对象时指定泛型的类型

public class MyGenericZiClassB extends MyGenericFuClass {

@Override

public void print(T t) {

System.out.println(t);

}

}

public static void main(String[] args) {

//创建对象指定泛型类型为String

MyGenericZiClassB gzB = new MyGenericZiClassB<>();

gzB.print("abc");

//gzB.print(100);//错误,只能传递String类型的参数

//创建对象指定泛型类型为Integer

MyGenericZiClassB gzB2 = new MyGenericZiClassB<>();

gzB2.print(100);

}

3. 泛型接口

3.1 定义格式:

权限修饰符 interface 接口名 {...}

例如,

public interface MyGenericInterface {

//抽象方法

public abstract void show(T t);

}

3.2 使用方式:

1、定义实现类的同时指定泛型接口上的的具体数据类型

public class MyGenericInterfaceImplA implements MyGenericInterface {

@Override

public void show(String s) {

System.out.println(s);

}

}

public class MyGenericInterfaceDemo1 {

public static void main(String[] args) {

MyGenericInterfaceImplA mi = new MyGenericInterfaceImplA();

// 已指定String类型,只能传字符串

mi.show("abc");

}

}

2、定义实现类时,若不指定接口上的泛型的具体数据类型,实现类也必须指定为泛型类,创建对象时,确定泛型的类型

public class MyGenericInterfaceImplB implements MyGenericInterface {

@Override

public void show(T t) {

System.out.println(t);

}

}

public class MyGenericInterfaceDemo2 {

public static void main(String[] args) {

//创建对象指定泛型类型为String

MyGenericInterfaceImplB mi = new MyGenericInterfaceImplB<>();

mi.show("abc");

//创建对象指定泛型类型为Integer

MyGenericInterfaceImplB mi2 = new MyGenericInterfaceImplB<>();

mi2.show(100);

}

}

4. 泛型类、泛型接口小结:

语法格式:

在创建泛型类、泛型接口的对象时,为泛型形参指定具体类型

在继承泛型类或实现泛型接口时,为泛型形参指定具体类型

异常类不能是泛型的

不能使用new E[]。但是可以:E[] array = (E[])new Object[size]; E[] array = (E[])new Object[]{...};等

父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:

子类不保留父类的泛型:按需传参

没有类型 擦除

具体类型

子类保留父类的泛型:泛型子类

全部保留

部分保留

结论:子类除了指定或保留父类的泛型,还可以增加自己的泛型(可类推到接口继承、接口实现)

class Father {

}

// 子类不保留父类的泛型

// 1)没有类型 擦除

class Son1 extends Father {// 等价于class Son extends Father{

}

// 2)具体类型

class Son2 extends Father {

}

// 子类保留父类的泛型

// 1)全部保留

class Son3 extends Father {

}

// 2)部分保留

class Son4 extends Father {

}

增加自己的泛型:

class Father {

}

// 子类不保留父类的泛型

// 1)没有类型 擦除

class Son extends Father{//等价于class Son extends Father{

}

// 2)具体类型

class Son2 extends Father {

}

// 子类保留父类的泛型

// 1)全部保留

class Son3 extends Father {

}

// 2)部分保留

class Son4 extends Father {

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值