写在前面:
视频是什么东西,有看文档精彩吗?
视频是什么东西,有看文档速度快吗?
视频是什么东西,有看文档效率高吗?
泛型方法
诸小亮:下面来看一下——泛型方法
张小飞:刚才说泛型类的时候,里面不是有泛型方法吗?
诸小亮:其实那个并不能称为泛型方法,真正的泛型方法:
定义方法时,使用:<泛型名>,那么这个方法就是泛型方法,比如:
public <Q> void print(Q q){
System.out.println("参数q,转换为字符串是:" + q);
}
张小飞:原来如此,这个应该怎么用呢?
诸小亮:来,看下面的代码
public class Demo {
public static void main(String[] args) throws Exception {
//1. print方法可以传递任何类型的数据
new Demo().print(new Hero("yase"));
new Demo().print(123);
new Demo().print("456");
}
//2. 定义一个可以打印任何类型的方法
public <Q> void print(Q q){
System.out.println("参数q,转换为字符串是:" + q);
}
}
结果:
张小飞:这么说,静态方法上也可以定义泛型吧
诸小亮:是的,静态方法也可以使用泛型:
张小飞:泛型方法,在工作中都有什么用呢?
诸小亮:工作中使用还是很多的,定义一个方法,该方法可以从 ArrayList 中取出对应的值
如果不用泛型:
//该方法从 list 中取出指定的字符串
public static String getObj(List<String> list, String arg){
for(String t : list){
if(t.equals(arg)){
return t;
}
}
return null;
}
//该方法从 list 中取出指定的Integer
public static Integer getObj2(List<Integer> list, Integer arg){
for(Integer t : list){
if(t.equals(arg)){
return t;
}
}
return null;
}
public static void main(String[] args) throws IOException {
ArrayList<String> list = new ArrayList<>();
list.add("妲己");
list.add("亚瑟");
list.add("李白");
String yase = getObj(list, "亚瑟");
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
Integer num = getObj2(list2, 3);
}
诸小亮:上面的代码中,你能看出什么?
张小飞:getObj、getObj2这两个方法非常相似,只是参数类型不同
诸小亮:没错,这时候就可以使用泛型方法,提高代码复用率
//定义一个泛型方法,从 list 中取出 跟参数 arg 相同的值
public static <T> T getObj(ArrayList<T> list, T arg){
for(T t : list){
if(t.equals(arg)){
return t;
}
}
return null;
}
public static void main(String[] args) throws IOException {
ArrayList<String> list = new ArrayList<>();
list.add("妲己");
list.add("亚瑟");
list.add("李白");
String yase = getObj(list, "亚瑟");
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
Integer num = getObj(list2, 3);
}
小结:使用泛型可以提高代码的复用性
泛型接口
诸小亮:在接口上使用:<泛型名>,那么这个接口就是泛型接口,比如:
interface MyInterface<T> {
void show(T t);
}
张小飞:嗯嗯,这个我知道,像 List、Set、Comparable 都是泛型接口
诸小亮:那你知道,应该怎么用泛型接口吗?
张小飞:在实现这个接口时候,指定具体的类型就行了,比如:
张小飞:上图,在实现接口时指定了String类型,那么复写 show 方法时,参数就必须是String类型
诸小亮:嗯嗯,很不错
张小飞:不过我有个疑问,如果实现接口时不想指定具体的类型呢?
诸小亮:如果不想指定具体类型,可以继续使用泛型,比如:
张小飞:明白了,这时候 MyInterfacedImpl 就是——泛型类
诸小亮:嗯,是这个道理
通配符
张小飞:通配符是什么?
诸小亮:通配符,也属于泛型的一种,用于限制类型
张小飞:限制类型?这是什么意思?
介绍
诸小亮:我们先定义一个通用的方法,打印集合中的元素
张小飞:这个简单,看我的
//打印指定集合中的元素
public static void printCollection(Collection<String> collection){
for(Iterator<String> it = collection.iterator();it.hasNext();){
System.out.println(it.next());
}
}
public static void main(String[] args) throws Exception {
List<String> list = new ArrayList<>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
printCollection(list);
}
诸小亮:上面的代码,打印的元素时 String 类型的集合,如果给你一个存储Integer的集合,printCollection 方法就不能用了,比如:
张小飞:这个简单,可以使用泛型啊
诸小亮:你说的不错,但是还有另一种方式
张小飞:哪种方式?
诸小亮:通配符,比如:**
张小飞:原来还可以这样,不过这跟 **T Q E **等泛型,有什么不一样吗?
诸小亮:都差不多,只不过 ‘?’ 的使用方式更宽泛一些
张小飞:这是什么意思?
泛型的限定(了解)
诸小亮:那,我们就要聊一聊——泛型的限定
张小飞:限定?
诸小亮:是的,用来限制是哪些类型
泛型上限
诸小亮:首先看——泛型上限,比如:List<? extends Hero> list = new ArrayList<>();
张小飞:这是什么意思?
诸小亮:这表示 list 只能接收 Hero 及其子类型的数据,这就是泛型的上限
张小飞:能具体演示一下吗?
诸小亮:当然可以,先准备一些代码
class Hero{
String name;
public Hero(String name){this.name = name;}
}
class Fashi extends Hero{
public Fashi(String name){ super(name);}
}
public class Demo {
public static void main(String[] args) throws IOException {
List<? extends Hero> list = new ArrayList<>();
}
张小飞:不对啊,为什么我这里不能给 list 添加数据?
诸小亮:这是正常的
张小飞:您不是说, list 只能存储 Hero 及其子类型的数据吗?为什么不能添加呢?
诸小亮:这就是泛型限定的缺陷了,虽然不能添加数据,但是可以这样:
List<Hero> heros = new ArrayList<>();
heros.add(new Hero("李白"));
heros.add(new Fashi("嫦娥"));
List<? extends Hero> list = heros;//直接给整个List赋值
张小飞:。。。。,完全不懂,这样的话,为什么要用泛型限定呢?
诸小亮:使用泛型限定,一般是作为方法的参数,限制数据类型,比如:
//该方法只接收List类型的容器,且容器中存储的只能是Hero或Hero的子类型数据
public static void printCollection(List<? extends Hero> collection){
for(Iterator it = collection.iterator(); it.hasNext();){
System.out.println(it.next());
}
}
结果:
张小飞:原来如此,明白了
泛型下限
诸小亮:刚才说的是泛型上限,接下来我们说说——泛型下限
张小飞:这个是???
诸小亮:不要着急,还是先准备代码
class Person{
}
class Hero extends Person{
String name;
public Hero(String name){this.name = name;}
}
class Fashi extends Hero{
public Fashi(String name){ super(name);}
}
public class Demo {
public static void main(String[] args) throws IOException {
//泛型下限
List<? super Hero> list = new ArrayList<>()
}
}
张小飞:这表示** list 只能存储 Hero 及其父类型**吗?
诸小亮:额。。。,是的,不过有些小问题
张小飞:什么意思?
诸小亮:使用 add 方法添加数据时,可以添加 Hero 及其子类型,比如:
张小飞:还真是,Fashi 是添加了一个 Hero 的子类
诸小亮:但是不能添加 Hero 的父类型,比如:
张小飞:这是为什么?
诸小亮:一会儿再给你解释,不过虽然不能使用 add 方法添加 Hero 的父类型,但是:
张小飞:。。。。,完全懵了
诸小亮:这就给你解释,看下面代码
public static void main(String[] args) throws IOException {
//泛型下限
//1. 使用 add 方法,只能添加 Hero 及其子类型
List<? super Hero> list = new ArrayList<>();
list.add(new Hero("yase"));
list.add(new Fashi("daji"));
//2. 使用整体赋值,只能是 Hero 及其父类型
test(new ArrayList<Hero>());
test(new ArrayList<Person>());
test(new ArrayList<Object>());
}
public static void test(List<? super Hero> list){
Object c = list.get(0);
}
具体解释:
前提:List<? super Hero> list:表示接受 Hero 及其父类型的对象
1. 为什么使用 add 可以添加 Hero 的子类对象
因为 Hero 子类对象,本质也是 Hero 类型,并且可以向上转型为Person、Object
2. 为什么使用 add 不能添加 Hero 的父类对象
因为 List<? super Hero> list = new ArrayList<Hero>(); 是合法的
上面的 test 方法中,参数 list 的值可能是 new ArrayList<Hero>(),
这样的话,添加 Person 就不合适了,所以。。。。。
张小飞:大概明白了
诸小亮:另外,获取元素时,由于无法确定从 list 中取出来的是什么类型,所以用 Object 接收