泛型

泛型

什么是泛型

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

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

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

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);  //不需要进行强制类型转换

使用泛型的优势

  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类型

  • 注意:声明一个泛型实例时,传递给形参的实参必须是类类型,而不能使用简单类型。如果不传递类型,则系统默认类型为Object.

带两个类型参数的泛型类

  • 如果引用多个类型,可以使用逗号分隔:<S, D>
    类型参数名可以使用任意字符串,建议使用有代表意义的单个字符,以便于和普通类型名区分,如:T代表type,有源数据和目的数据就用S/D,子元素类型用E等。当然,你也可以定义为XYZ,甚至xyZ。
    泛型中的类型参数严格说明集合中装载的数据类型是什么和可以加入什么类型的数据,记住:Collection 和 Collection 是两个没有转换关系的参数化的类型

集合类中定义范型

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

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

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

有界类型

public class MyClass<T extends Number>{  }<T extends Number>
//表示允许传入给T的类型必须是Number类型的子类型或者Number类型
MyClass<Integer> mc=new MyClass<Integer>();  // 语法正确,因为Integer是Number的子类型
MyClass<String> mc=new MyClass<>(); // 语法错误,因为String不是Number类型
  • 声明泛型可以给泛型添加约束
    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<T extends Number>

  • 接口充当上界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<String>();

通配符参数的语法使用

- <? extends E>上限通配符,用来限制类型的上限

public class B<T>{
  public void pp(List<? extends T> list){}
    //表示list中元素的类型必须是T类型或者T类型的子类或者T接口的实现类
  public void cc(List<T> list){} 
   //要求list中的元素必须是T类型的,假设有实参ArrayList<T子类>传入这个方法则语法报错
}

解决方法:

- <? super E>下限通配符,用来限制类型的下限

public class B<T>{
  public void pp(List<? super T> list){}
  //表示list中元素的类型必须是T类型或者T类父类或者T接口的实现类或者T的父接口实现类
}
  • 在泛型类或者泛型接口中不允许直接使用super
//Integer可以认为是Integer的父类
List<? super Interger> array=new ArrayList<Integer>();
//Number是Integer的父类 
List<? super Integer> array=new ArrayList<Number>();
//Object 是Integer的父类
List<? super Integer > array=new ArrayList<Object>();
  • 频繁往外读取内容的,适合用上界extends
  • 经常往里插入的,适合用下界Super

泛型方法

  • 所谓泛型方法,就是带有类型参数的方法。
  • 它既可以定义在泛型类中(例如public void show(T aa),在使用泛型上没有任何特殊语法要求)。
  • 也可以定义在普通类中(需要自定义参数类型,那么把泛型参数放在方法上就可以了,就是放在返回值类型之前,例如public <T> void show(T aa))。
  • 静态方法不能访问类的泛型,如果需要泛型只能在方法上使用泛型,例如public static <T> void show(T aa) 在泛型类中包含泛型的方法.
  • 在普通类中包含泛型的静态方法,注意在返回类型之前的是必须的,不是返回类型.
public class Test{
	public static <T> void pp(T tt){
	}
}
  • 在泛型类中的静态方法上也必须包含,否则编译错误
public class Test<T>{
	public static <T> void pp(T tt){
	}
}
  • 一个方法如果被声明成泛型方法,那么它将拥有一个或多个类型参数,不过与泛型类不同,这些类型参数只能在它所修饰的泛型方法中使用。

泛型类的继承

  • 泛型类也是可以继承的,任何一个泛型类都可以作为父类或子类
  • 不过泛型类与非泛型类在继承时的主要区别在于:泛型类的子类必须将泛型父类所需要的类型参数,沿着继承链向上传递。这与构造方法参数必须沿着继承链向上传递的方式类似。
public class Fa<T>{
	private T id;
	public void setId(T id){
		this.id=id;
	}
	public void show(){
		Sysytem.out.println(id.getClass());
	}
	public void main(String args[]){
		Fa ff=new Son();
		ff.getId(123);
		ff.show;
	}
}
class Son extends Fa<String>{
//明确指定父类中的泛型,子类中的类型才能确定
}
  • 子类不是泛型类:需要给父类传递类型常量,如class AA extends A<String>
public class Fa<T>{
	private T id;
	public void setId(T id){
		this.id=id;
	}
	public void show(){
		Sysytem.out.println(id.getClass());
	}
	public void main(String args[]){
		Fa ff=new Son();
		ff.getId(123);
		ff.show();
	}
}
class Son extends Fa{
}
  • 子类是泛型类:可以给父类传递类型常量,也可以传递类型变量。如class AA3<E> extends A<E>
public class Fa<T>{
	private T id;
	public void setId(T id){
		this.id=id;
	}
	public void show(){
		Sysytem.out.println(id.getClass());
	}
	public void main(String args[]){
		Son<Integer> ff=new Son<>();
		ff.getId(123);
		ff.getId("123");//语法报错,因为传入的参数应为Integer类型
		ff.show();
	}
}
class Son<D> extends Fa<D>{
//Son<D>用于声明Son类中有个泛型为D,这里的Fa<D>经Son的D继续向上传递
}
  • 如果父类中泛型有约束,在子类声明泛型时就必须有对应的约束
public class A1<T extends Serializable>{}
public class B1<D extends Serializable> extends A1<D>{}//声明类型时在子类上声明约束
//可以写为:
public class B1<D extends Serializable> extends A1{}//此时A1中的T就是Object类型的

泛型的局限性

  • 不能使用基本类型
  • 不能使用泛型类异常
  • 不能使用泛型数组不能实例化参数类型对象。例如T ob = new T();
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值