前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >泛型接口,泛型类和泛型通配符

泛型接口,泛型类和泛型通配符

作者头像
用户5745563
发布2019-07-04 14:53:24
2.2K0
发布2019-07-04 14:53:24
举报
文章被收录于专栏:码思客码思客
java零基础入门-高级特性篇(六) 泛型 中

泛型的使用位置,除了最常见的约束集合元素,还可以使用在接口,类,方法上面。最本质的原因就是为了在使用接口,类,方法的时候,可以将类型作为参数,进行类型的参数传递。这样可以使程序的编写更加的灵活,在创建对象,调用方法的时候动态的指定类型,所以泛型也可以理解为类型的参数化。

类型参数化

光看名字,又不好理解,通俗点可以这样理解。定义方法,接口的时候可以传递参数,参数通常都是指定类型的,比如

public void add(Student student);

public void add (Teacher teacher);

这里的student是Student类型的参数,teacher是Teacher 类型的参数,他们已经指定好类型了。那么类型的参数化,就是指将类型作为参数传递进方法。比如

public void add(E e);

这里的add方法并没有指定任何一个具体的类型,而是将类型也作为了参数,E是任何一个类型,e是任意类型E的实例。如果传递进来的类型参数是Student,那么这个方法就是add(Student student);,如果传递进来的类型参数是Teacher ,那么这个方法就是add (Teacher teacher);

类型参数化

当使用泛型定义参数的时候,每一个传递进来的类型参数,就创建了一个该方法的版本,add(Student student)是一个add(E e)的版本,add (Teacher teacher)也是一个add(E e)版本。类型参数化的好处是使代码变得更加灵活,原因就在于此,因为可以通过对类型的抽象,使代码匹配各种不同有具体类型版本的需求。

泛型接口和泛型类

泛型接口的定义,public interface man<T>{...}。在接口名后面加上泛型类型参数T,这样就定义了一个泛型接口。

泛型接口

在接口中定义的类型参数可以在接口中当做类型使用,任何需要类型的地方都可以使用类型参数替代。比如传递的类型是Teacher,那么run(T t)就是老师在跑路,getObject()方法返回一个老师对象,getAll(String name)方法可以根据学校名字获取所有老师。加入传递的是Student,那么上面三个方法分别是学生在跑路,获取一个学生对象,根据学校名称返回所有学生。使用泛型接口,可以在实现的时候才定义具体需要实现的类型,使接口可以进行更高级的抽象。

泛型类的定义,public class Man<T>{...},在类名后面加上泛型类型参数T,这样就定义了一个泛型类。

泛型类

和泛型接口不同,类有构造器,并且构造器也可以使用泛型类型参数。在这个泛型类里面,使用了两个泛型类型参数,如果有必要可以定义更多的泛型参数。

如果java里面没有继承这个特性,那么泛型到这里就讲完了,但是,正因为java有继承这个特性,会导致很多其他的问题出现,其复杂程度会几何级的上升,后面的知识点对抽象能力和思维能力有较高的要求,请做好战斗准备。

下面从集合开始,先来思考几个前面没有思考过的问题。

1.如果集合加上了泛型,那么如果添加的元素是泛型的子类或者父类能添加进去吗?

添加子类父类

上面例子可以看出,如果泛型类型有子类,添加泛型类型的子类是可以的,但是如果泛型类型有父类,往集合添加泛型类型的父类会出现编译错误。因为子类继承了父类的所有方法,所以如果添加的是子类,当从集合取出的元素调用泛型类型的方法也不会有什么问题。但是如果定义的是子类的泛型集合,放入的是父类元素,当要使用子类方法的时候,父类元素可能没有,那么就会发生错误,所以泛型是子类型的话,是不允许加入父类型元素的。

2.再看另一个问题,如果父类是泛型类型,如何定义子类?

泛型类的子类

如果将一个类定义为泛型类,那么在创建该泛型类的子类的时候不能将子类直接继承该泛型类,而是需要指定父类泛型的类型。比如父类是Book<T>,子类不能直接extends Book<T>,而是需要指定T的类型,上例中使用的Book<Double>作为类型。

在java中,泛型不能继承和实现。为什么?WHY?请手动滑动到本章最上面,跟我一起念,类型参数化。问题的关键就在这里,因为泛型将类型作为一种参数,而参数是什么?在定义方法的时候,他不需要具体指定是什么数据,但是一旦你调用使用这个方法,就必须指定这个参数具体是什么。

使用泛型

由于方法中的泛型需要在定义类的时候就指定,所以如果需要使用含有泛型的方法,必须在创建该泛型类对象的时候就需要指定泛型类型,因为使用的时候必须指定类型,不论是普通参数还是泛型参数。那为什么继承的时候也要确定泛型呢?因为继承就是在使用一个已经定义好的类,使用泛型类,就要指定类型。

3.用什么样的参数形式来接受List<Book>这种形式的参数?

现在需要为所有List抽象一个方法,不论给的参数是List<Book>,List<String>,都可以接收并且打印List中的元素。是不是理所当然的想到了List<Object>?用List<Object>来接收参数就行了嘛。

泛型类型不匹配

啪啪啪,脸是不是很疼。显然这样是不可以的,错误提示参数类型不匹配,Object是所有类型的父类,但是List<Object>并不是List<Book>的父类,那应该使用什么方法达到上面的要求呢?泛型提供了一个泛型通配符用于接收所有类型的泛型类型。

泛型的通配符

通配符

泛型的通配符可以很好的解决所有泛型类型父类的问题,使用<?>来作为类或接口的泛型参数,这样就可以抽象出泛型类的父类。比如用List<?>可以看做所有List的父类,Set<?>可以看做所有Set的父类,使用?来表示一个未定义的类型,用来接受任何类型参数。

但是如果使用通配符,在部分功能上是会受到限制的。

1.只能通过Object遍历集合。在访问通配符泛型List<?>的时候,集合里的元素只能当做Object来访问,因为在定义的时候只是一个通配符,不是具体类型,所以不能进行类型转换只能作为Object访问。

2.不能使用add方法。List提供的add(E e)方法是需要指定类型的,这里不是E吗?这是个泛型类型啊?为什么要提供类型?因为这是定义,一旦要使用add(E e)方法,必须指定具体的类型。定义通配符以后,在使用通配符的方法里是不知道类型的,所以不能使用add方法。

不能用add方法

就算是Object类型也不能使用add方法,为什么?假设可以添加,会发生什么问题?如果我使用List<Book>作为参数,传入到printAllObject方法,运行完打印元素的语句后,会往List<Book>类型的集合里面新增一个Object类型的对象,而Object又是Book类型的父类,上面说过,泛型类型的父类型元素不能添加到该集合,所以这里就算是Objcet类型也不能添加。所以使用泛型通配符的话,这个集合的作用就是使用Object类型来遍历它。

上面第二点,如果集合使用了泛型通配符,要往集合添加Object是不允许的,因为无论最后来的是什么类型,Object都是这个类型的父类,所以不允许添加Object类型。那么如果我可以保证添加一个元素,一定是泛型类型的子类,那么是不是可以添加元素了?这个问题就涉及到泛型通配符的上下限问题了。下章继续。

本章有很多类名称相同,但是内容不同,请在不同的包下进行操作。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-12-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码思客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档