泛型

不使用泛型,使用集合时编程比较复杂

List list=new ArrayList();
list.add(123);  //自动向上转型    int-->Integer-->Object
//获取数据还需要窄化操作
int kk=(Integer)list.get(0);  //不进行类型判断,有可能出现ClassCastException

使用泛型,可以将运行时的类型检查搬到编译期实现;同时获取数据时不需要再编码进行类型转换

List<Integer> list=new ArrayList<>();  //从JDK1.7+支持泛型推导
list.add(123);
list.add(new Random());//编译报错
int kk=list.get(0);  //不需要进行强制类型转换

什么是泛型

泛型是jdk5引入的类型机制,就是将类型参数化,泛型作为一种安全机制而产生

泛型在本质上是指类型参数化。所谓类型参数化,是指用来声明数据的类型本身,也是可以改变的,它由实际参数来决定。在一般情况下,实际参数决定了形式参数的值。而类型参数化,则是实际参数的类型决定了形式参数的类型。

使用泛型的优势

1、可读性,从字面上就可以判断集合中的内容类型;

2 、类型检查,避免插入非法类型。

3 、获取数据时不在需要强制类型转换。

泛型类

所谓泛型类generic class就是具有一个或多个类型参数的类

public class 类型名<泛型名称列表>{ }

public class Student<ID> {
	private ID id;
	public ID getId() {
		return this.id;
	}
	public void setId(ID id) {
		this.id=id;
	}
}

声明一个泛型类Generic,使用泛型的语法为<名称>,在类中就可以通过名称直接使用这个类型

Student<String> s1=new Student<>();
//	s1.setId(123); 编译报错,因为123不是String类型
	s1.setId("123");
	
	Student<Integer> s2=new Student<>();
//	s2.setId("123");编译报错,因为"123"不是Integer类型
	s2.setId(123);

如果定义了泛型类,但是引用时不声明泛型值系统则识别泛型为Object类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KVxqa56b-1614257820598)(C:\Users\17655\Desktop\1\1.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5pAtMp1L-1614257820602)(C:\Users\17655\Desktop\1\2.png)]

带两个类型参数的泛型类

如果引用多个类型,可以使用逗号分隔:<S, D>

类型参数名可以使用任意字符串,建议使用有代表意义的单个字符,以便于和普通类型名区分,如:T代表type,有源数据和目的数据就用S/D,子元素类型用E等。当然,你也可以定义为XYZ,甚至xyZ。

泛型中的类型参数严格说明集合中装载的数据类型是什么和可以加入什么类型的数据,记住:Collection 和 Collection 是两个没有转换关系的参数化的类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NGL8IVOI-1614257820604)(C:\Users\17655\Desktop\1\3.png)]

集合类中定义范型

范型只能使用引用类型,而不能使用基本类型,即:List是错误的

保持良好的编程风格,尽量使用范型

List<String> list=...
Set<Integer> set=new HashSet<>();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ghi5iBpE-1614257820606)(C:\Users\17655\Desktop\1\4.png)]

另外写法[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lJyukGaV-1614257820608)(C:\Users\17655\Desktop\1\5.png)]

有界类型

public class MyClass{ }表示允许传入给T的类型必须是Number类型的子类型或者Number类型

MyClass mc=new MyClass(); 语法正确,因为Integer是Number的子类型

MyClass mc=new MyClass<>(); 语法错误,因为String不是Number类型[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rbUagvyJ-1614257820610)(C:\Users\17655\Desktop\1\6.png)]

声明泛型可以给泛型添加约束

Java提供了有界类型bounded types。在指定一个类型参数时,可以指定一个上界,声明所有的实际类型都必须是这个超类的直接或间接子类。class classname

public class Oper<T extends Number> {
//不确定个数的参数,进行加减操作。所以参与计算的必须是Number类型
	public double add(T...params) {
		double res=0;
		if(params!=null && params.length>0) {
			for(T tmp:params)
				res+=tmp.doubleValue();
		}
		return res;
	}
	public double sub(T...params) {
		double res=0;
		if(params!=null && params.length>0) {
			for(T tmp:params)
				res-=tmp.doubleValue();
		}
		return res;
	}
}

调用泛型类

public class Test1 {
	public static void main(String[] args) {
		Oper<Float> op=new Oper<>();
		double res=op.add(1f,2f,3f,4f,5f,6f,7f);
		System.out.println(res);
		
		Oper<Long> op2=new Oper<>();
		res=op2.sub(1L,2L,3L,4L,5L,6L,7L);
		System.out.println(res);
	}
}

接口和类都可以用来做上界。

类充当上界public class Oper

接口充当上界class Stats这里需要注意:针对上界接口使用的关键字仍然是extends而非implements

实现冒泡排序

//要求传入T的类型必须实现了Comparable接口
public class Oper<T extends Comparable<T>> {
	public void sort(List<T> list) {
		if(list!=null && list.size()>1) {
			for(int i=1;i<list.size()-1;i++) {
				for(int k=0;k<list.size()-i;k++) {
					T c1=list.get(k);
					T c2=list.get(k+1);
					if(c1.compareTo(c2)>0) {  //因为要求T必须实现了Comparable接口,所以T中就有Comparable接口中的方法实现
						list.set(k, c2);
						list.set(k+1, c1);
					}
				}
			}
		}
	}
}
Oper<Integer> op = new Oper<>();
		List<Integer> list = new ArrayList<>();
		Random r=new Random();
		for(int i=0;i<10;i++)
			list.add(r.nextInt(100));
		op.sort(list);
		list.forEach(System.out::println);

随机生成字符串

private static final String source="qwertyuiopasdfghjklzxcvbnm1234567890";
	public String generateString(int len) {
		StringBuilder sb=new StringBuilder();
		Random r=new Random();
		for(int i=0;i<len;i++)
			sb.append(source.charAt(r.nextInt(source.length())));
		return sb.toString();
	}

一个类型参数可以有多个限界,如class Stats<T extends Comparable & Serializable>。限界类型用&分隔,因为逗号用来分隔类型参数。在多个限界中,可以有多个接口,但最多只能有一个类。如果用一个类作为限界,它必须是限界列表中的第一个。

通配符参数

使用一般用于接收参数

void doSomething(Stats<?> ob) 表示这个参数ob可以是任意的Stats类型。其中?表示一个不确定的类型,它的值会在调用时确定下来。 List<?> list = new ArrayList(); [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dHPplp21-1614257820611)(C:\Users\17655\Desktop\1\7.png)]

通配符参数的语法使用