java – 理解泛型

java泛型相比C++模板要简单的多得多,只不过java泛型标准引入的时候因为历史版本兼容性的原因受到了一些限制,我们大可不必拘泥于刻板的语法强调说明,让我们一起来把握一下java泛型最重要的那些部分。

代码:https://github.com/owenliang/java-generic-demo

定义普通类

Person是基类,Student继承Person,ClassLeader继承Student。

多态1

子类Student向上转换为基类Person,这个是面向对象基础。

多态2

上述Student[]可以赋值给Person[],在C++里需要循环逐个赋值,而java竟然可以整体转换比较新奇。

泛型1

虽然Student可以向上转换为Person,但是List<Student>是不能向上转换为List<Person>的,这完全是两个不同的数据类型。

泛型2

?用于通配类型,因此List<?> b可以接纳任意类型的List。

因为List<?> b里面的数据类型是不明确的(虽然我们眼睛看得出来,但这并没有什么卵用),所以向b内添加任何类型的对象都是不允许的。

同样道理,从List<?> b取出来的元素类型也不明确,我们只能确信它可以向上转换为Object基类。

泛型3

List<? extends Person> b对?通配符进行了进一步约束,即泛型类型必须是Person的子类,比如List<Student>就满足该通配约束。

面对List<? extends Person> b来说,其数据类型已经不明确了,唯一可以确信的是该类型继承自Person,也就是说它可能是List<Student>、List<ClassLeader>或者其他我们不知道的Person子类。

在这种对List数据类型不完全明确的情况下,会存在这些可能的case:

  • 如果List<? extends Person> b实际类型是List<Student> b,那么我们add(new ClassLeader())是没有问题的。
  • 但如果List<? extends Person> b实际类型是List<ClassLeader> b,那么我们add(new Student())肯定是编译不过的。

当我们用?把编译器的眼睛蒙上之后,其实编译器也不知道实际情况是哪个case,所以就干脆都不让add好了。


get容易理解,因为List<? extends Person>约束了?必须是Person的子类,所以取出来的对象可以向上转换为Person(任何Person的父类型当然也可以)。

泛型4

List<? super Student> b 对通配符?进行了进一步约束,即泛型类型必须是Student的父类,这里就是Person和Object满足条件。

因为List<? super Student> b的实际类型一定是Student的父类型,因此所以继承自Student的子类都可以通过向上转换类型被add到b列表内。

我们可以枚举一些case:

  • 假设List<? super Student> b实际类型是List<Person>,那么new Student()和new ClassLeader()都可以向上转换类型完成add。
  • 如果Student有2个父类,除了extends Person之外还implements了Child接口,那么:
    • 假设List<? super Student> b实际类型是List<Person>,那么new Person()是没有问题的。
    • 假设List<? super Student> b实际类型是List<Child>,那么new Person()去add肯定编译不过,因为Person和Child是平行关系,虽然它俩都是Student的父类。

你会发现上述加粗的部分是<? super Student>无法明确的2种case,因此当我们向List<? super Student>添加任意Student的父类对象是一定编译不过的,因为不明确。

但是添加Student的任意子类都是OK的,因为它们一定可以向上转换为Student的任意父类型。


get容易理解,因为List<? super Student>一定是Student的父类型,但是也不明确到底是哪个父类型,所以get的时候只能用Object接住。

总结

我们应该按实际需求考虑用什么语法:

  • 通配符?如果不做任何约束,那么它是完全不明确的,不能往里add任何东西,取出来的只能当做Object。
  • 通配符<? extends A>只接受A类的子类,但是因为不明确具体是哪个子类,所以add不了任何东西,但是get出来都可以向上转换为A类。
  • 通配符<? super A>只接受A类的父类,但是因为不明确具体是A的哪个父类,所以只能add A或者A的子类,因为它们可以都向上转换为A的任意父类,同时get因为不明确是A的哪个父类所以只能向上转换为Object来接收。

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~