java泛型介绍

泛型是什么?
泛型本质是指类型参数化。意思是允许在定义类、接口、方法时使用类型形参,当使用时指定具体类型,所有使用该泛型参数的地方都被统一化,保证类型一致。如果未指定具体类型,默认是Object类型。集合体系中的所有类都增加了泛型,泛型也主要用在集合。

泛型的定义
泛型类:public class Demo {} ,T表示未知类型。
泛型接口:public interface ImplDemo<T,V>{} ,和定义类一样(接口就是一个特殊类)。
泛型方法:public void demo1(T name){System.out.println(name);} , public T demo2(T t){ return t;}

泛型的好处

编译时确定类型,保证类型安全,避免类型转换异常。
避免了强制类型转换。
代码利于重用,增加通用性。

泛型的限制和规则

泛型的类型参数只能是引用类型,不能使用值类型。
泛型的类型参数可以有多个。
泛型类不是真正存在的类,不能使用instanceof运算符。
泛型类的类型参数不能用在静态申明。
如果定义了泛型,不指定具体类型,泛型默认指定为Ojbect类型。
泛型使用?作为类型通配符,表示未知类型,可以匹配任何类型。因为是未知,所以无法添加元素。
类型通配符上限:<? extends T>,?代表是T类型本身或者是T的子类型。常用于泛型方法,避免类型转换。
类型通配符下限。<? super T>,?代表T类型本身或者是T的父类型。
除了通配符可以实现限制,类、接口和方法中定义的泛型参数也能限制上限和下限。

泛型代码实例
接下来,使用代码来演示泛型的用途,建议使用目录查看具体内容。

集合类演示泛型

	//未指定泛型
	TreeSet ts = new TreeSet();
	ts.add(10);
	ts.add(25);
	ts.add("30");
	System.out.println(ts);//运行时报错,类型转换异常
	
	//mode 2
	TreeSet<Integer> ts2 = new TreeSet<>();
	ts2.add(10);
	ts2.add(25);
	ts2.add("30"); //编译器提示报错,无法添加非Integer类型

未使用泛型时,可以添加任意元素,因为TreeSet会比较每一个元素,所以运行时会引发类型转换异常。使用泛型后,只能添加同一个类型,所以不会出错。

定义泛型类

public class Person {
private T name;//类型是未知的

public Person(T name) {
	this.name = name;
}	

public T getName() {
	return name;
}

public void sexName(T name) {
	this.name = name;
}

}

在上面实例中,Person类定义成泛型类,成员变量name的类型指定为T,表示未知类型。实例化该类对象后,可以看到name的类型是Object,表示可以接收任何类型。

	Person p = new Person(10);	//new Person(Object name)

加上泛型后

	//使用泛型两种方式
	Person<String> ps = new Person<String>(); //new Person<String>(String name)
	Person<String> ps = new Person<>();//new Person<>(T name)

第一种,会直接确定参数类型是什么,而第二种的参数类型是T ,但如果加入的非String类型,编译器会检查并报错。两者区别不大。在JDK1.7之后使用第二种,会自动检查泛型,可以省略后部分<>的泛型参数,建议使用第二种。

定义泛型接口

interface A{
void display(T value);
T getValue(T v);
}

//未对泛型接口指定具体类型
public class Person implements A{

@Override
public void display(Object obj) {
	System.out.println();
}

@Override
public Object getValue(Object v) {
	return null;
}

}

如果我们定义了泛型,不指定具体类型,默认就是Object类型。当我们为泛型接口指定具体类型后,代码如下:

//泛型接口
interface A{
void display(T value);
T getValue(T v);
}

//为泛型接口指定具体类型
public class Person implements A{
@Override
public void display(String value) {
}
@Override
public String getValue(String v) {
return null;
}
}

泛型接口指定具体类型后,所有使用了该泛型参数的地方都被统一化。其实泛型接口和泛型类是一样的写法。

定义泛型方法

先使用常规方法进行对比。

public static void main(String[] args) {
	int[] arr = new int[] {1, 8, 15, 6, 3};
	double[] douArr = {10.5, 25.1, 4.9, 1.8};
	String[] strArr = {"我","是","字","符","串"};
	forArr(strArr);
	
}

//遍历数组的重载方法,支持int和double类型
public static void forArr(int[] arr) {
for(int i=0; i<arr.length; i++) {
System.out.println(arr[i]);
}
}
//重载了
public static void forArr(double[] arr) {
for(double d : arr) {
System.out.println(d);
}
}
//……
//……

如上所示,如果想遍历Stirng类型数组,那就还要再次重载代码,如果是八种类型都有,代码量非常庞大。使用泛型方法全部通用,代码如下:

public static void main(String[] args) {
	Integer[] arr =  {1, 8, 15, 6, 3};
	Double[] douArr = {10.5, 25.1, 4.9, 1.8};
	String[] strArr = {"我","是","字","符","串"};
	
	forArrGenric(strArr);
	
}
//泛型方法
public static <T> void forArrGenric(T[] arr) {
	for(int i=0; i < arr.length; i++) {
		System.out.println(arr[i]);
	}
}

只需定义一个泛型方法,根据运行时传入的参数类型,动态地获取类型,就能做到遍历所有类型数组。但需要注意,泛型的类型参数只能是引用类型,值类型无法在泛型中使用,所以上面的数组都改成了引用类型。值类型需要使用对应的包装类类型。

使用类型通配符
使用之前,先使用常规方式来进行比较。

public static void main(String[] args) {
	HashSet hs = new HashSet();
	hs.add("A");
	hs.add("QQ");
	hs.add("Alipay");

	new Test2().test2(hs);
}	
//普通遍历Set集合,Set是泛型接口,没指定具体泛型参数会引起警告
public void test(Set s) {
	for(Object o : s)
		System.out.println(o);
}	
//增加泛型参数,参数类型是Set<Object>
public void test2(Set<Object> s) {
	for(Object o : s)
		System.out.println(o);
}

方法参数的Set集合使用了泛型参数,方便将参数类型转换成Object,看起来没什么错。当传入一个带泛型参数的集合时,会出现编译错误。代码如下:

public static void main(String[] args) {
	HashSet<String> hs = new HashSet();
	hs.add("A");
	hs.add("QQ");
	hs.add("Alipay");
	new Test2().test2(hs); //error
}	
//增加泛型参数,参数类型是Set<Object>
public void test2(Set<Object> s) {
	for(Object o : s)
		System.out.println(o);
}

因为泛型类不是真正存在的类,所以Set和Set不存在关系,自然无法作为参数传入进去。这时我们就可以使用类型通配符,如下:

//使用类型通配符作为类型参数
public void test2(Set<?> s) {
	for(Object o : s)
		System.out.println(o);
}

Set<?>表示可以匹配任意的泛型Set。虽然可以使用各种泛型Set了。但弊端就是类型未知,所以无法添加元素。还有范围过于广泛,所以这时可以考虑限制的类型通配符。

限制的类型通配符
上面代码只要是泛型Set都允许被遍历,如果只想类型通配符表示一个类和其子类本身呢?设置类型通配符上限,代码如下:

public class Test2 {
public static void main(String[] args) {
ArrayList ar = new ArrayList<>();
List lt = new ArrayList<>();
List lStr = new ArrayList<>();
demo(ar);
demo(lt);
demo(lStr); //error
}
//限制的类型通配符
public static void demo(List<? extends Test2> t) {
for(int i = 0; i < t.size(); i++) {
System.out.println(t.get(i));
}
}
}
class Test3 extends Test2{}//子类

<? extends T>:表示类型是T本身或者是T类型的子类类型。 <? super T>:表示类型是T类型本身或者是T类型的父类类型。叫做类型通配符的下限。使用方式都差不多。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

116000815

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值