第十四章 泛型

泛型不只是 Java 语言所特有的特性,泛型是很多编程语言的一种特性。它允许开发人员在编写代码时定义一些可变部分(数据的数据类型),这部分可变的内容在使用时才做出明确指定(指定具体的数据类型)。我们可以这样理解泛型,它的本质是对数据的操作进行封装,而忽略数据因类型造成的差异化。例如,我们定义一个方法,要对两个数据进行加法操作,由于数据类型的不同,我们需要定义N个不同数据类型的形参方法,这样显然不太合理。如果我们能够使用一个“通配符”来代替这些数据,然后直接定义对这些通配符数据进行操作的单一方法,而真正调用这些方法的时候,再确定这些通配符数据的数据类型。这样的设计才是最合理的,也就是泛型的目的。

Java泛型有三种常用的使用方式:泛型类,泛型接口和泛型方法。

泛型类:  在类名后定义通配符,只能用在成员变量上。

泛型接口:在接口名后定义通配符,只能用在抽象方法上。

泛型方法:在方法名之前定义通配符,可以用于方法的返回类型或者方法的参数上。

三者的区别就是位置不同,作用也就不同。我们先介绍泛型类的使用,代码示例如下:

// 泛型类,T表示“任意类型”
class Test<T> {
	
	// T数据类型的成员变量 val
	private T val;

	// 有参构造方法
	public Test(T val) {
		this.val = val;
	}
	
	// 输出变量val
	public void show() {
		System.out.println(val.toString());
	}
}

上述代码中,我们定义Test类时引入了一个通配符T,用尖括号(<>)括起来,并放在类名的后面。这个T就是泛型字母,可以随意指定,尽量使用单个的大写字母。泛型类可以声明多个通配符。例如,class Test <T,U> { … } 逗号间隔就行了。当我们使用Test类的时候,就要用具体的数据类型替换掉通配符T,代码示例如下:

// 泛型类使用
Test<String> a = new Test<String>("abc");
a.show();	// 输出 abc
Test<Integer> b = new Test<Integer>(123);
b.show();	// 输出 123

看到这里,我们基本上对泛型有了初步的认识啦。接下来介绍泛型接口的使用,

// 泛型接口,T表示“任意类型”
interface ITest<T> {
	
    public T val();
}

// CTest类继承泛型接口
class CTest implements ITest<String>{

}

泛型接口的使用和泛型类很相似,只不过接口的泛型字母只能用于方法。然后我们使用一个类去继承这个接口,然后实现接口里面的方法,Eclipse可以帮助我们快速完成,如下图:

点击40行出的红叉叉,会弹出几个选项,点击第一个选项,添加未继承实现的方法,Eclipse就会自动帮我们向CTest类中添加一个val方法,我们稍作修改,如下所示:

@Override
public String val() {
	return "abc";
}

接下来就不用说了,我们直接使用CTest类就可以了。

// 使用CTest类
CTest c = new CTest();
System.out.println(c.val());

接下来,我们介绍泛型方法的使用,它可以作为方法返回类型,也可以作为参数,如下:

// 泛型方法
public static <T> void test1(T t) {

    System.out.println(t.toString());
}

// 泛型方法
public static <T> T test2(T... t) {

    return t[0];
}

请注意,第一个泛型方法中,泛型字母只用于方法参数,第二个泛型方法中,泛型字母不仅仅作为参数,还作为方法的返回类型。当作为方法参数的是时候,T…”代表数组类型。请注意,以上两个泛型方法是在普通类中定义的,不是在泛型类中定义的哦。接下来,我们看看这两个泛型方法的使用,代码如下:

// 泛型方法的使用
test1(new Integer(100));
test2(new String("abc"));

// 泛型方法的使用
Integer[] arr = {1,2,3,4,5};
Integer r1 = test2(arr);
System.out.println(r1);

String[] str = {"a","b","c","d","e"};
String r2 = test2(str);
System.out.println(r2);

有时候,我们希望对泛型表示的数据类型做范围的约定,这种约定可以分为上限和下限两种,分别使用<T extends className>和<T super className>来表示,前者T所代表的类型是className类型的子类,后者T所代表的类型是className类型的父类。代码示例如下:

// 泛型上限,约束泛型的范围
public static <T extends List> void test3(T t) {

    System.out.println("方法参数必须是List或其子类");
}

我们定义了方法的参数,使用泛型字母T表示任意一个数据类型,但是由于我们使用了泛型上限,因此这个T代表的数据类型只能是List或List的子类。我们之前讲解过,List的子类包括ArrayList和LinkedList等等。该方法test3的使用如下所示:

// 泛型上限,方法参数必须是List或其子类
test3(new ArrayList());
test3(new HashSet());
test3(new LinkedList());

请大家注意,我们故意写了一个HashSet的参数,从而导致代码无法编译,说明我们定义test3方法的时候使用泛型上限起作用了。我们注释掉HashSet所在行的代码,就可以编译运行啦。关于泛型下限我们就不再举例说明了。

接下来,我们在说一个特殊的泛型字母:?通配符。我们这样理解它和T通配符的区别。T 是一个不确定的类型,通常用于泛型类和泛型方法的定义,而?也是一个不确定的类型,通常在使用泛型的时候用来代表一个不确定的类型。前者是定义,后者是使用,代码示例如下:

// 通配符 ? 的使用
Test<?> t1 = null;
t1 = new Test<Integer>(123);
t1 = new Test<String>("abc");

// 不使用通配符 ?
Test<Integer> t2 = new Test<Integer>(123);
Test<String> t3 = new Test<String>("abc");

我们可以看到,当我们使用?通配符去声明一个Test类的对象t1的时候,我们可以将其示例化为各种类型的Test实例对象,非常的方便。而如果我们不使用?通配符的话,就只能固定类型的声明并实例化同一种数据类型的对象啦。

其实泛型的应用中,最广泛的还是和集合的搭配使用。我们知道集合可以存放任意的数据类型,这样虽然看似很好,但是就造成了访问任意元素的时候,需要进行类型转换。因此,我们日常使用集合的时候,都会像数组一样,存储同一种数据类型的元素。这个时候,我们使用泛型对集合元素的数据类型做约束的话,我们在访问元素的时候,就自动类型转换了。

// 集合不使用泛型
List list1 = new ArrayList();
list1.add("123");
// 需要类型转换
String ele1 = (String)list1.get(0);
System.out.println(ele1);

// 集合中使用泛型
List<String> list2 = new ArrayList<String>();
list2.add("456");
// 不需要类型转换
String ele2 = list2.get(0);
System.out.println(ele2);

// 迭代器中使用泛型
Iterator<String> it = list2.iterator();
while(it.hasNext()) {
	// 不需要类型转换
	String temp = it.next();
	System.out.println(temp);
}

另外,?通配符也可以搭配上限和下限来使用,我看如下代码:

// 集合中使用?通配符
List<? extends Number> l1 = new ArrayList<Integer>();
List<? extends Number> l2 = new ArrayList<Long>();
List<? extends Number> l3 = new ArrayList<String>();

请注意,我们将集合List中的元素限制为数字类型,因此最后一行的String是错误的,因此整个代码无法编译运行,我们必须注释掉String所在行的代码,才能成功编译运行。

本课程涉及的代码可以免费下载:
https://download.csdn.net/download/richieandndsc/85645935​​​​​​​

今天的内容就讲的这里,我们来总结一下。今天我们主要讲了Java的泛型。本章节重点在于了解泛型的意义,就是参数化数据类型,定义的时候使用通配符表示,使用的时候才确定真正的数据类型。泛型类,泛型接口和泛型方法的三种方式大家一定要了解,即便不会使用,也要能够看懂他人的泛型代码。最后就是泛型和集合的搭配使用,这是重点!好的,谢谢大家的收看,欢迎大家在下方留言,我也会及时回复大家的留言的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值