Java——泛型基本总结(通配符、泛型接口、泛型方法)

举报
Winter_world 发表于 2021/09/28 22:43:45 2021/09/28
【摘要】 目录 1、泛型的产生背景 2、泛型的通配符? 3、泛型接口(重点) 4、泛型方法 5、总结 JDK1.5后的三大主要特性:泛型、枚举、Annotation。 1、泛型的产生背景 假如,现在定义一个表示坐标的类,Point,属性坐标x,y,但是此类设计特殊,现在由于设计特殊,现在实际使用中有可能出现以下三种结构的数据: ...

目录

1、泛型的产生背景

2、泛型的通配符?

3、泛型接口(重点)

4、泛型方法

5、总结


JDK1.5后的三大主要特性:泛型、枚举、Annotation。

1、泛型的产生背景

假如,现在定义一个表示坐标的类,Point,属性坐标x,y,但是此类设计特殊,现在由于设计特殊,现在实际使用中有可能出现以下三种结构的数据:

  • 整数:x=10、y=10;
  • 小数:x=10.1、y=10.1;
  • 字符串:x=东经10度、y=北纬20度。

可以发现,Point中可以保存三种数据类型,而Point类中只存在xy两个属性。现在唯一可以想到的数据类型就是Object类型,因为满足以下转换:

保存int:int-》自动装箱为Integer-》向上转型为Object;

保存double:double-》自动装箱为Double-》向上转型为Object;

保存字符串:String-》向上转型为Object。

【第一步】:定义Point类


  
  1. class Point {
  2. private Object x;
  3. private Object y;
  4. public Object getX() {
  5. return x;
  6. }
  7. public void setX(Object x) {
  8. this.x = x;
  9. }
  10. public Object getY() {
  11. return y;
  12. }
  13. public void setY(Object y) {
  14. this.y = y;
  15. }
  16. }

【第二步】:设置&取出数据


  
  1. Point p = new Point();
  2. //整型数据
  3. p.setX(10);
  4. p.setY(10);
  5. int x1 = (Integer)p.getX();
  6. int y1 = (Integer)p.getY();
  7. System.out.println("x="+x1+",y="+y1);
  8. //double数据
  9. p.setX(10.1);
  10. p.setY(10.1);
  11. double x2 = (Double)p.getX();
  12. double y2 = (Double)p.getY();
  13. System.out.println("x="+x2+",y="+y2);
  14. //字符串数据
  15. p.setX("东经10度");
  16. p.setY("北纬20度");
  17. String x3 = (String)p.getX();
  18. Stringy3 = (String)p.getY();
  19. System.out.println("x="+x3+",y="+y3);

以上的操作虽然满足了要求,但是整个代码的实现关键在于利用了Object类型,利用Object操作的优点是可以接收所有的数据类型,但是Object类要接收数据类型,必须进行强制的向下转型,存在隐患:


  
  1. p.setX(100);
  2. p.setY("北纬20度");
  3. String x3 = (String)p.getX();
  4. String y3 = (String)p.getY();
  5. System.out.println("x="+x3+",y="+y3);

以上代码编译OK,但是项目运行时会发生错误:

以上分析可知,向下转型这种操作本身存在安全隐患问题,且不能通过编译检查出来,用Object类进行这类处理就说有这种问题。那么该问题如何解决呢?唯一的解决方案就是不进行对象的向下转型。这样的背景下,就产生了泛型的技术。

泛型的本质:类中的属性或方法的参数,不设置具体的类型,只使用一个标记表示,而在类使用的时候才对其动态的绑定一种数据类型。

【举例】:使用泛型


  
  1. class Point<T> { //T:Type,P:Param,R:Return
  2. private T x;
  3. private T y;
  4. public T getX() {
  5. return x;
  6. }
  7. public void setX(T x) {
  8. this.x = x;
  9. }
  10. public T getY() {
  11. return y;
  12. }
  13. public void setY(T y) {
  14. this.y = y;
  15. }
  16. }

此时,Point类中的属性类型无法确定,必须在类实例化对象的时候动态的绑定。

【举例】:使用泛型


  
  1. Point<String> p = new Point<>();
  2. p.setX("东经10度");
  3. p.setY("北纬20度");
  4. String x = p.getX();
  5. String y = p.getY();
  6. System.out.println("x="+x+",y="+y);

此时我们设置数据时,数据类型不是String,编译不通过,且取出数据时也不用进行强制向下转型,消除了安全隐患。

2、泛型的通配符?

现在假设有如下泛型类:


  
  1. public class MainActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. Message<String> msg = new Message<>();
  6. fun(msg);
  7. System.out.println(msg.getInfo());
  8. }
  9. public static void fun(Message<String> temp){
  10. temp.setInfo("HELLO");
  11. }
  12. }
  13. class Message<T>{
  14. private T info;
  15. public T getInfo() {
  16. return info;
  17. }
  18. public void setInfo(T info) {
  19. this.info = info;
  20. }
  21. }

泛型类型可以改变,将以上使用的泛型类型由String 变为Integer,此时fun方法不能正常使用,且由于重载是受到参数类型而不是泛型类型的限制,那么无法通过重载来解决此问题。那么该如何解决,需要一种标记满足以下要求:

  • 可以用于泛型上,这样可以避免安全警告;
  • 标记使用后,允许接收任何内容,但是不能修改里面的数据;

为此,泛型中提供了一个重要的通配符“?”

【举例】:使用通配符描述


  
  1. public class MainActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. Message<Integer> msg = new Message<>();
  6. msg.setInfo(100);
  7. fun(msg);
  8. }
  9. public static void fun(Message<?> temp){
  10. System.out.println(temp.getInfo());
  11. }
  12. }

但是,此通配符基础上又扩展处理两个子通配符组合:

  • ?extends 类:设置泛型的上限,可以设置在类或方法参数中;
          ?extends Number:表示可以使用的泛型只能是Number或者Number的子类;
  • ?super 类:设置泛型的下限,可以设置在方法参数中:
          ?super String:表示只能够设置String或者其父类。

【举例】:设置泛型的上限(只能设置数字)


  
  1. public class MainActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. Message<Integer> msg = new Message<>();
  6. msg.setInfo(1000);
  7. fun(msg);
  8. }
  9. public static void fun(Message<? extends Number> temp){
  10. System.out.println(temp.getInfo());
  11. }
  12. }
  13. class Message<T extends Number>{
  14. private T info;
  15. public T getInfo() {
  16. return info;
  17. }
  18. public void setInfo(T info) {
  19. this.info = info;
  20. }
  21. }

【举例】:设置泛型 的下限


  
  1. public class MainActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. Message<String> msg = new Message<>();
  6. msg.setInfo("1111");
  7. fun(msg);
  8. }
  9. public static void fun(Message<? super String> temp){
  10. System.out.println(temp.getInfo());
  11. }
  12. }
  13. class Message<T>{
  14. private T info;
  15. public T getInfo() {
  16. return info;
  17. }
  18. public void setInfo(T info) {
  19. this.info = info;
  20. }
  21. }

后续看开发文档时,会看见很多这样的通配符标记,我们要看的懂,我们要掌握?的作用是什么。

3、泛型接口(重点

在接口上用泛型,就是泛型接口。


  
  1. interface IMessage<T>{
  2. public void print(T t);//方法上使用泛型
  3. }

以上,实现 了泛型接口,但是对于泛型接口的子类,有两种实现形式:

  • 模式一:子类继续使用泛型声明

  
  1. public class MainActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. MessageImpl<String> msg = new MessageImpl<>();
  6. msg.print("hello");
  7. }
  8. interface IMessage<T>{
  9. public void print(T t);//方法上使用泛型
  10. }
  11. class MessageImpl<P> implements IMessage<P>{
  12. @Override
  13. public void print(P p) {
  14. System.out.println(p);
  15. }
  16. }
  17. }

模式二:子类定义时不使用泛型,直接为父接口设置好泛型类型


  
  1. public class MainActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. MessageImpl msg = new MessageImpl();
  6. msg.print("hello");
  7. }
  8. interface IMessage<T>{
  9. public void print(T t);//方法上使用泛型
  10. }
  11. class MessageImpl implements IMessage<String>{
  12. @Override
  13. public void print(String p) {
  14. System.out.println(p);
  15. }
  16. }
  17. }

以上两种实现模式要掌握,非常重要。

4、泛型方法

若在一个方法使用了泛型,这个方法就称为泛型方法。


  
  1. interface IMessage<T>{
  2. public void print(T t);//方法上使用泛型
  3. }

泛型方法不一定还要定义在泛型声明的一个类中,也可能就是一个方法定义为泛型方法。


  
  1. public class MainActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. Integer num[] = fun(1,2,3);
  6. for (Integer n:num){
  7. System.out.println(n);
  8. }
  9. }
  10. public static <T> T[] fun(T ... arg){ //声明并返回泛型
  11. return arg;
  12. }
  13. }

现实来讲,泛型方法能看懂即可。

5、总结

1)泛型解决的问题就是向下转型所带来的安全隐患;

2)泛型的本质:类的属性或方法的参数可以由用户在使用时动态设置;

3)通配符?、? extends 类、? super 类。

 

 

作于202005112145,已归档

———————————————————————————————————

本文为博主原创文章,转载请注明出处!

若本文对您有帮助,轻抬您发财的小手,关注/评论/点赞/收藏,就是对我最大的支持!

祝君升职加薪,鹏程万里!

文章来源: winter.blog.csdn.net,作者:Winter_world,版权归原作者所有,如需转载,请联系作者。

原文链接:winter.blog.csdn.net/article/details/106061267

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。