12-集合框架(泛型限定)
接下来说一说泛型的高级应用。
例子:
将al传进printColl中打印:
将al1传入printColl打印:
发现挂掉了:
必须挂的,因为左右两边类型不匹配:
可是我们又需要打印Integer类型的数据,难道要再写一个方法吗?好麻烦呢。
那有没有办法打印任意类型的数据呢?
有这样一种方法:当我们的对象类型不确定时,我们可以通过一个通配符来表示:
虽然也可以这样写(比如早期没出现泛型的时候都是这样写):
这样写即使传入使用泛型的对象也是可以的,但是这样写不够严谨,我们还是在这里写一个占位符更严谨。
现在我们再试一下:
全部都顺利打出来了:
也可以不用<?>用,但是方法名前面一定也要写上哦:
那<?>和有什么区别吗?
有的,不过不是很大。
是一个具体类型的话,就可以接收并操作这个类型,比如这样:
但是如果写成<?>,就无法进行这个操作。
代表一个具体类型,传什么类型就是什么类型,而?是不明确类型,叫做占位符,告诉你我这里有泛型,但是泛型是什么不知道。
不明确具体类型的情况下用<?>表示。
我们这里依然用<?>来表示,现在有一个问题,我们是否可以直接打印长度?
不可以。
为什么呢?
因为代表不明确类型,而.length()是一个具体类型的方法。如果传Integer,它就没有这个方法。
所以用泛型有好处,可以提高扩展性,但是它不可以使用类型特有方法。
但是toString()方法就可以,因为所有类型都具备这个方法:
总之,类型调用方法这里我们要注意一下。就像多态,提高了扩展性,但是也有弊端:不能预先使用子类中的特有方法。
新的例子:
Person类:
定义一个容器al,存入Person对象:
运行,一切正常:
现在来了一个学生类,它继承了Person类:
然后我们继续定义一个新的容器并存入Student:
编译:
失败了。
主函数中最后一句代码:
printColl(al1);
就相当于:
ArrayList al=new ArrayList();
这个是不允许的。
为什么不允许呢?Student不是Person的子类吗?
拿动物和猪(猪是动物的子类)打个比方吧:
ArrayLis<动物> al=new ArrayList<猪>();
我们定义的是要存动物(等号左边),而真正的窝里面存的是猪(等号右边)。我们考虑到了定义的集合是可以存储动物的(等号左边),于是找了条狗想要存进去,可是窝里面只能装猪(等号右边),狗进去之后类型就冲突了,再往出取的时候就很容易出现类型安全问题。(还是不太懂,之前讲多态的时候,这样不是都没有问题吗?而且我理解的是这次存的是猪,下次存的是狗,并不是一次存入不同的,感觉也不矛盾?)
道理其实很简单,把狗扔到猪圈里,猪就疯了。
那如果这样写呢:
ArrayList al=new ArrayList();
这样也不行。
总之,左右两边一定要一致。
那我们这样写是不是就可以了:
这样既可以接收Person对象,也可以接收Student对象。
但是这样一来,除了Person和Student对象,其他的什么类型的对象也都可以进来了。而我们现在就只想用这个方法来打印Person和Person的子类,其他类我们不想受理。
这个时候就来了泛型限定:
它表示可以接收Person类型和Person的子类类型。
运行:
OK了。
这就是我们所说的泛型限定,而泛型限定分为两种:上限和下限。
总结一下:
?:通配符,也可以理解为占位符。
? extends E:可以接收E类型或者E的子类型。这是上限。
? super E:可以接收E类型或者E的附类型。这是下限。
什么时候用到下限呢,我们在TreeSet的构造方法中发现了:
这是什么意思呢?
举个例子:
也可以这样写:
Person可以接收Student吗?
可以。
我们如果比较this.getName()的话,Person也具备这个方法:
比较器:
我们再返回看一下构造函数:
那写Person也可以:
这样写之后,我们依然可以接收Student对象:
所以不管是传Student类型还是Person类型都可以,它们都可以接收Student对象。
这就是下限的例子。
在这个方法中,既可以接收Person也可以接收Person的子类型,限定了上限:
而比较的时候,我们限定了下限,既能比较Student,也能比较Person:
我们可以在TreeSet中看到几个方法:
这个的意思是,往里面添加的时候,只要是E或者E的子类型,都可以操作E,多态嘛。
而这个也是一样,比较的时候,比较的类型、或者比较的父类型,都能够接收子类对象进来。
这里还有个的例子:
这个方法上并没有定义泛型,而是用<?>来表示占位符。而泛型定义在类上了:
上面的意思是,我要比较另一个集合的时候,这个集合不确定,传进去什么集合就是什么集合。不写<?>也可以,但是在1.5版本以后,Colletion后面都带着<>,所以还是写个占位符放在里面,类型不明确的情况下,用?来表示就可以了。
13-集合框架(泛型限定2)
再一个例子。
Person类和它的子类Student类:
但是这样打印是没有意义的,因为学生对象不具备比较性。
我们现在要写一个比较器:
再看一下TreeSet接口,它传入的是:
构造方法中,可以接收E类型和E的父类型:
所以比较器写是对的。
继续:
将比较器传进去:
运行:
OK了。
接下来Worker类继承了Person类:
新写了一个Worker比较器:
主函数中存入Worker对象并打印:
运行也是没有问题的:
但是好麻烦,每次搞一个对象都要搞一个比较器。
我们可以直接在比较其中写它们的父类型(因为构造函数中可以接收E及E的父类型):
它的子类们直接使用这一个比较器就可以了:
运行都是OK的,图略。
但是有一点要注意,比较器中使用的只能是父类的方法。有扩展性就有局限性。
网友评论