Java 进阶——泛型
1、简介
泛型的设计背景
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList这个就是类型参数,即泛型。
泛型的概念
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变员、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。
从JDK1.5以后,Java引入了“参数化类型( Parameterized type)”的概念,允许我们在创建集合时再指定集合元素的类型,正如: List,这表明该List只能保存字符串类型的对象。
JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参。
java 中泛型标记符:
- E - Element (在集合中使用,因为集合中存放的是元素)
- T - Type(Java 类)
- K - Key(键)
- V - Value(值)
- N - Number(数值类型)
- ? - 表示不确定的 java 类型
2、泛型使用实例
集合中使用泛型的情况
package fanxing;
import org.junit.Test;
import java.util.*;
public class Demo01 {
//以ArrayList为例
@Test
public void test() {
//泛型的类型不能是基本数据类型
ArrayList<Integer> list = new ArrayList<>();
list.add(12);
list.add(22);
list.add(32);
list.add(42);
// list.add("wanli");编译不通过,会进行类型检查,保证数据的安全
//forEach遍历
for (Integer score : list) {
int stuScore = score;
System.out.println(stuScore);
}
//迭代器遍历
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
int stuScore1 = iterator.next();
System.out.println(stuScore1);
}
}
//以HashMap为例
@Test
public void test1(){
HashMap<Integer, String> map = new HashMap<>();
map.put(1,"wanli");
map.put(2,"shenming");
map.put(3,"taoxian");
map.put(4,"xuanyu");
//遍历所有的key
Set<Integer> keySet = map.keySet();
Iterator<Integer> iterator = keySet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
//遍历所有的value
Collection<String> values = map.values();
Iterator<String> iterator1 = values.iterator();
while (iterator1.hasNext()){
System.out.println(iterator1.next());
}
//遍历所有的key-value
Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
Iterator<Map.Entry<Integer, String>> iterator2 = entrySet.iterator();
while (iterator2.hasNext()){
System.out.println(iterator2.next());
}
}
}
3、自定义泛型结构
3.1、自定义泛型类和泛型接口
泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。
自定义泛型类和自定义泛型接口的区别主要在与类和接口的区别。
自定义泛型类
public class Order<T> {
private int id;
private String name;
//类的内部结构可以使用类的泛型
T orderT;
public Order(){
}
public Order(int id,String name,T orderT){
this.id = id;
this.name = name;
this.orderT = orderT;
}
public T getOrderT(){
return orderT;
}
public void setOrderT(T orderT){
this.orderT = orderT;
}
}
自定义泛型类的使用
//如果自定义了泛型类,在实例化时要指明类的泛型,如果不指明就默认泛型类型是Object
Order<String> order = new Order<>(1,"wanli","开朗");
order.setOrderT("害羞");
System.out.println(order.getOrderT());//害羞
自定义或使用泛型类泛型接口注意点
1、泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,2,E3>
2、泛型类的构造器如下: public GenericClass()。而这种是错误的: public GenericClass(){}。
3、实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
4、泛型不同的引用不能相互赋值。
5、泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。泛型要使用一路都用。要不用,一路都不要用。
6、如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。
7、jdk1.7,泛型的简化操作: ArrayList flist = new ArrayList<>();
8、泛型的指定中不能使用基本数据类型,可以使用包装类替换。
9.在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。
10.异常类不能是泛型的
11.不能使用new E[]。但是可以:E]elements =(E[])new Object[capacity];
12、父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
-
子类不保留父类的泛型,子类自己按需实现:
-
子类没有泛型,父类泛型被擦除
public class SubOrder extends Order{ } //等价于public class SubOrder extends Order<Object>{} -
子类指定具体泛型类型
public class SubOrder extends Order<Integer>{ }
-
-
子类保留父类泛型:
-
全部保留
public class SubOrder<T1,T2> extends Order<T1,T2>{ } -
部分保留
public class SubOrder<T2> extends Order<Integer,T2>{ }
-
3.2、自定义泛型方法
方法也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。
//泛型方法所属的类是或不是泛型类都没有关系,泛型方法可以声明为静态
public <E> List<E> copy(E[] arr){
ArrayList<E> list = new ArrayList<>();
for(E e : list){
list.add(e);
}
return list;
}
泛型方法使用实例:
Order<String> order = new Order<>();
Integer[] arr = new Integer[]{1,2,3,4};
List<Integer> list = order.copy(arr);
System.out.println(list);//[1, 2, 3, 4]
定义泛型方法的规则:
- 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的 )。
- 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
- 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
- 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像 int、double、char 等)。
4、通配符的使用
类型通配符一般是使用 ? 代替具体的类型参数。例如 List<?> 在逻辑上是 List,List 等所有 List<具体类型实参> 的父类。
通配符使用实例
@Test
public void test2(){
ArrayList<String> list = new ArrayList<>();
ArrayList<Integer> list1 = new ArrayList<>();
list.add("wanli1");
list.add("wanli2");
list.add("wanli3");
list1.add(1);
list1.add(2);
list1.add(3);
print(list);
print(list1);
}
public void print(List<?> list){
Iterator<?> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
查看结果:

使用通配符后数据读写要求
ArrayList<String> list = new ArrayList<>();
ArrayList<Integer> list1 = new ArrayList<>();
//使用通配符后,以ArrayList<?>为例,就不能向其内部添加数据,null除外
ArrayList<?> list2 = new ArrayList<>();
// list2.add("a");
list2.add(null);
list.add("wanli1");
list.add("wanli2");
list.add("wanli3");
list2 = list;
//使用通配符后,以ArrayList<?>为例,允许读取数据,返回的数据类型为Object
Object o = list2.get(1);
System.out.println(o);//wanli2
有限制的通配符的使用
通配符指定上限:
上限extends:使用时指定的类型必须时继承某个类,或者实现某个接口,即<=
通配符指定下限:
下线super:使用时指定的类型不能小于操作的类,即>=
举例:
- <? extends Number> 范围:(无穷小,Number]
只允许泛型为 Number 及 Number的子类的引用调用
- <? super Number> 范围:[Number,无穷大)
只允许泛型为Number及Number父类的引用调用
- <? extends Comparable>
只允许泛型为实现omparable接口的实现类的引用调用
本文详细介绍了Java中的泛型概念、使用实例、自定义泛型类和接口以及泛型方法。泛型允许在定义类和接口时指定类型参数,提高代码的类型安全性和复用性。通过泛型类和泛型方法,可以限制容器中存储的元素类型。同时,文章还探讨了通配符的使用,展示了如何在不同场景下灵活运用泛型以实现更精确的数据操作。
1007

被折叠的 条评论
为什么被折叠?



