1. 背景

泛型技术诞生之前(jdk5以前),创建集合的类型都是object 类型的元素,存储内容没有限制,编译时正常,运行时容易出现classcastexception 异常。

public class test {
	public static void main(string[] args) {
		arraylist list = new arraylist();
		list.add("java");
		list.add(100);
		list.add(true);
		for(int i = 0;i <list.size();i++) {
			object o = list.get(i);
			string str = (string)o;
			system.out.println(str);
		}
	}
}

输出:

java
exception in thread “main” java.lang.classcastexception: java.lang.integer cannot be cast to java.lang.string
at com.chengyu.junit.test.main(test.java:18)

2. 泛型概念

jdk5 中引入泛型,从而可以在编译时检测是否存在非法的类型数据结构
其本质就是参数化类型,可以用于类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法

public static void main(string[] args) {
		arraylist<string> strlist = new arraylist<string>();
		strlist.add("java");
		strlist.add("c#");

		for(int i = 0;i < strlist.size();i++) {
			string str = strlist.get(i);
			system.out.println(str);
		}
	}

3. 泛型类

3.1 定义与调用

定义类时设置泛型,该泛型可用于类中的属性或方法中,调用该泛型类时,指定具体类型;

// 调用泛型类
public class generictest {
	public static void main(string[] args) {
		generic<string> strgen = new generic<>("a");
		string key = strgen.getkey();
		system.out.println(key);
	}
}
// 定义泛型类
// @param <t> 使用类时指定
class generic<t>{
	private t key;
	public generic(t key) {
		this.key = key;
	}
	public t getkey() {
		return key;
	}
	public void setkey(t key) {
		this.key = key;
	}
	@override
	public string tostring() {
		return "generictest [key=" + key + "]";
	}
}

3.2 注意

1)调用泛型类时未定义类型,则会按照object 类型处理;
2)调用时分别指定不同类型,但本质都是object 类型;
3)泛型不支持基本数据类型;
4)泛型类的成员方法不可以用static 修饰(泛型方法可以)。

3.3 使用

需求:抽奖活动,但抽奖内容没有确定,可能都是现金,也可能都是物品

public class productgettertest {
	public static void main(string[] args) {
		// 抽物品
		productgetter<string> strproductgetter = new productgetter<>();
		string[] str = {"手机","电视","洗衣机"};
		for(int i = 0; i < str.length; i ++ ) {
			strproductgetter.addproduct(str[i]);
		}
		string strproduct = strproductgetter.getproduct();
		system.out.println("恭喜您抽中了" + strproduct);

		system.out.println("=============================================");
		// 抽现金
		productgetter<integer> intproductgetter = new productgetter<>();
		integer[] integer = {1000,2000,3000};
		for(int i = 0; i < integer.length; i ++ ) {
			intproductgetter.addproduct(integer[i]);
		}
		int intproduct = intproductgetter.getproduct();
		system.out.println("恭喜您抽中了" + intproduct);
	}
}
// 抽奖器
class productgetter<t>{
	random random = new random();
	// 奖品池
	arraylist<t> list = new arraylist<>();
	// 添加奖品
	public void addproduct(t t) {
		list.add(t);
	}
	// 抽奖(获取奖品)
	public t getproduct() {
		 return list.get(random.nextint(list.size()));
	}
}

3.4 泛型类的继承

3.4.1 子类也是泛型类

子类也是泛型类,则泛型要保持一致。

class childfirst<t> extends parent{ ... }

1)指定子类泛型,不指定父类泛型,父类默认为object 类型

class parent<e>{
	private e value;
	public e getvalue() {
		return value;
	}
	public void setvalue(e value) {
		this.value = value;
	}
}
class childfirst<t> extends parent{
	@override
	public object getvalue() {
		return super.getvalue();
	}
}

2)若父类保留原有泛型,与子类泛型不一致,则会编译出错

class childfirst<t> extends parent<e>{
	@override
	public e getvalue() {
		return super.getvalue(); 
	}

3)父类泛型与子类保持一致
具体泛型指定是由子类传递到父类当中,所以继承时父类要与子类泛型保持一致(当然都写成e也可以)。

class parent<e>{
	private e value;
	public e getvalue() {
		return value;
	}
	public void setvalue(e value) {
		this.value = value;
	}
}
class childfirst<t> extends parent<t>{
	@override
	public t getvalue() {
		return super.getvalue();
	}
}

4)调用

public class generictest {
	public static void main(string[] args) {
		childfirst<string> childfirst = new childfirst<>();
		childfirst.setvalue("chengyu");
		system.out.println(childfirst.getvalue());
	}
}

5)补充:
子类可以进行泛型扩展,但子类必须有一个泛型与父类一致

public class generictest {
	public static void main(string[] args) {
		childfirst<string,integer> childfirst = new childfirst<>();
		childfirst.setvalue("chengyu");
		system.out.println(childfirst.getvalue());
	}
}
class parent<e>{
	private e value;
	public e getvalue() {
		return value;
	}
	public void setvalue(e value) {
		this.value = value;
	}
}
class childfirst<t,e> extends parent<t>{
	@override
	public t getvalue() {
		return super.getvalue();
	}
}

3.4.2 子类不是泛型类

子类不是泛型类,父类要明确泛型的数据类型

class childsecond extends parent<string>{ ... }

1)子类不是泛型类,不指定父类泛型,父类默认为object 类型

class parent<e>{
	private e value;
	public e getvalue() {
		return value;
	}
	public void setvalue(e value) {
		this.value = value;
	}
}
class childsecond extends parent{
	@override
	public object getvalue() {
		return super.getvalue();
	}
}

2)父类要明确泛型的数据类型

class childsecond extends parent<string>{
	@override
	public string getvalue() {
		return super.getvalue();
	}
}

3)调用

public class generictest {
	public static void main(string[] args) {
		childsecond childsecond = new childsecond();
		childsecond.setvalue("chengyu2");
		system.out.println(childsecond.getvalue());
	}
}

4. 泛型接口

4.1 定义

public interface generator<t>{ ... }

4.2 使用(与继承特点相同)

4.2.1 实现类不是泛型类

实现类不是泛型类,接口要明确数据类型

class apple implements generator<string>{
	@override
	public string getkey() {
		return "generator interface";
	}
}

4.2.2 实现类也是泛型类

实现类也是泛型类,实现类和接口的泛型类型要一致

class apple<t> implements generator<t>{
	private t key;
	@override
	public t getkey() {
		return key;
	}
}

5. 泛型方法

5.1 定义

5.1.1 泛型方法

修饰符 <t,e,..> 返回值类型 方法名(形参列表){
}

// 泛型方法
public <e,t> e getproduct(arraylist<e> list) {
	return list.get(random.nextint(list.size()));
}

5.1.2 静态泛型方法

修饰符 <t,e,..> 返回值类型 方法名(形参列表){
}

// 泛型方法
public <e,t> e getproduct(arraylist<e> list) {
	return list.get(random.nextint(list.size()));
}

5.1.3 可变参数的泛型方法

public <e> void print(e... e) {
	for(int i = 0; i < e.length;i++){
		system.out.println(e[i]);
	}
}
// 调用
print(1,2,3,4);

5.2 注意

1)包含泛型列表的方法才是泛型方法,泛型类中使用了泛型的方法并不是泛型方法;
2)泛型列表中声明了泛型类型,才可以在方法中使用泛型类型;
3)泛型方法中的泛型类型独立于泛型类的泛型,与类泛型类型无关;
4)泛型方法可以用static 修饰(泛型类的成员方法不可以)。

5.3 使用

5.3.1 定义泛型方法

// 抽奖器
// @param <t>
class productgetter<t>{
	random random = new random();
	// 奖品池
	arraylist<t> list = new arraylist<>();
	// 添加奖品
	public void addproduct(t t) {
		list.add(t);
	}
	// 抽奖(获取奖品)
	public t getproduct() {
		 return list.get(random.nextint(list.size()));
	}
	// 泛型方法
	public <e> e getproduct(arraylist<e> list) {
		return list.get(random.nextint(list.size()));
	}
}

5.3.2 调用泛型方法

public class productgettertest {
	public static void main(string[] args) {
		productgetter<integer> intproductgetter = new productgetter<>();

		arraylist<string> strlist = new arraylist<>();
		strlist.add("手机");
		strlist.add("电视");
		strlist.add("洗衣机");

		string product = intproductgetter.getproduct(strlist);
		system.out.println("恭喜您抽中了" + product);
	}
}

6. 类型通配符

6.1 类型通配符介绍

类型通配符一般用【?】代替具体的类型 实参

6.2 为什么要用类型通配符

泛型类被调用时,需要指定泛型类型,当泛型类的方法被调用时,不能灵活对应多种泛型类型的需求,如下面代码【4.】部分所示:

public class boxtest {
	public static void main(string[] args) {
		// 3.调用showbox
		box<number> box1 = new box<>();
		box1.setfirst(100);
		showbox(box1);
		// 4.再次调用showbox
		// 出现问题:类型发生变化后会报错
		box<integer> box2 = new box<>();
		box2.setfirst(200);
		showbox(box2);
	}
	// 2. 调用泛型类,此时需要指定泛型类型
	public static void showbox(box<number> box) {
		number first = box.getfirst();
		system.out.println(first);
	}
}
// 1.定义泛型类
class box<e>{
	private e first;
	public e getfirst() {
		return first;
	}
	public void setfirst(e first) {
		this.first = first;
	}
}

解决上述问题,类型通配符【?】登场

public class boxtest {
	public static void main(string[] args) {
		// 3.调用showbox
		box<number> box1 = new box<>();
		box1.setfirst(100);
		showbox(box1);
		// 4.再次调用showbox
		// 问题得以解决
		box<integer> box2 = new box<>();
		box2.setfirst(200);
		showbox(box2);
	}
	// 2. 调用泛型类,此时需要指定泛型类型
	// 【?】类型通配符等成
	public static void showbox(box<?> box) {
		object first = box.getfirst();
		system.out.println(first);
	}
}
// 1.定义泛型类
class box<e>{
	private e first;
	public e getfirst() {
		return first;
	}
	public void setfirst(e first) {
		this.first = first;
	}
}

6.3 泛型通配符上限 extends

【6.2】代码例中,虽然使用了通配符,但 box.getfirst()返回类型仍然需要定义成object 类型,并不理想。

public static void showbox(box<?> box) {
	object first = box.getfirst();
	system.out.println(first);
}

通配符上限登场:
调用showbox 方法时,传递的泛型类型可以是number 及其子类(integer)

public static void showbox(box<? extends number> box) {
	number first = box.getfirst();
	system.out.println(first);
}

注意:

public static void showbox(box<? extends number> box) {
	number first = box.getfirst();
	// 此处编译报错:类型不一致
	// 虽然定义上限,showbox 被调用时没问题,但在方法内同时定义多种类型,编译器无法识别
	integer second = box.getfirst();
	system.out.println(first);
}

6.4 泛型通配符下限 super

public static void showbox(box<? super integer> box) {
	number first = box.getfirst();
	system.out.println(first);
}

注意:
遍历时要用object 类型进行遍历;

7. 类型擦除

泛型信息只存在于代码编译阶段,进入jvm之前,与泛型相关的信息会被擦除,这个行为称为类型擦除。

到此这篇关于深入理解java泛型generic的文章就介绍到这了,更多相关java泛型内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!