当前位置: 首页 > news >正文

泛型进阶之通配符

通配符

通配符其实就是一个问号(?),多用于泛型中。

1.1 通配符所要解决的问题

首先先来看一段代码:

class Message<T> {private T message ;public T getMessage() {return message;}public void setMessage(T message) {this.message = message;}
}
public class Test2 {public static void main1(String[] args) {Message<String> message = new Message<>() ;message.setMessage("666");fun(message) ;}public static void fun(Message<String> temp){System.out.println(temp.getMessage());}
}

上面代码本身没有语法错误也可以正常编译运行,打印666,不过存在一定的局限性,当前的fun方法的参数被明确指定为了Message<String>类型,意味着它只能接收Message类中泛型为String类型的对象。

如果需要输出Message类的其他类型的对象,如Message<Integer>,就需要重载一个参数为对应类型fun方法。这么做代码的复用性就会变得很低了。

我们需要的解决方案:可以接收所有的泛型类型,但是又不能让用户随意修改。这种情况就需要使用通配符进行处理了:

修改代码如下:

  //可以接受任意类型,但是由于不知道类型不能进行删改public static void fun(Message<?> temp){System.out.println(temp.getMessage());}

注意此时如果在fun方法内部对当前message的值进行设置或者修改都是行不通的,如图: 这是因为当前的方法参数不确定类型,不能随意进行删改。

总结:使用了通配符之后,既使当前方法可以接收任意类型的数据功能,也使当前的形参在方法中无法随意修改。

public static void main(String[] args) {Message<String> message = new Message<>() ;message.setMessage("Java");fun(message) ;Message<Integer> message1 = new Message<>() ;message1.setMessage1(666);fun(message);}//可以接受任意类型,但是由于不知道类型不能进行删改public static void fun(Message<?> temp){System.out.println(temp.getMessage());}

运行结果如下: 

在“?”(通配符)的基础上又产生了两个子通配符:

?extends 类名:设置通配符上界

?super 类名:设置通配符下0界

1.2 通配符上界

语法:

<? extends 上界>

如<? extends Number>(当前传入的类型只能是Number或者Number的子类,如:Integer、Float、Double……)

通配符上界不仅能实现指定传入数据为数字类型,也可以指定实现指定传入类型为自定义类型。下面,我们就通过代码来实现:输出Fruit和Fruit子类的信息。

首先,要实现的关系图如下:

class Plate{@Overridepublic String toString() {return "一个盘子";}
}
class Food extends Plate{public String toString() {return "一种食物";}
}
class Fruit extends Food {public String toString() {return "一种水果";}
}
class Apple extends Fruit {public String toString() {return "一个苹果";}
}
class Banana extends Fruit {public String toString() {return "一个香蕉";}
}

注意:这里为了使输出的信息更加清晰,重写了toString方法。 

message类实现输出当前传入对象信息:

class Message<T> {private T message ;public T getMessage() {return message;}public void setMessage(T message) {this.message = message;}
}

funExtend方法实现输出信息功能: 

 public static void funExtend(Message<? extends Fruit> temp){System.out.println(temp.getMessage());//对于通配符的上界来说    是不可以修改元素的//temp.setMessage(new Banana());// temp.setMessage(new Apple());//向上转型Fruit fruit = temp.getMessage();}

由于只是给通配符加上了上界,本质上还是通配符,所以在当前funExtend方法中仍然无法修改参数的值。

测试代码:

    public static void main(String[] args) {Message<Apple> message = new Message<>() ;message.setMessage(new Apple());Message<Banana> message1 = new Message<>() ;message1.setMessage(new Banana());funExtend(message);funExtend(message1);}

我们要实现的是:输出Fruit和Fruit子类的信息 。如果我们在测试用例中传入一个Food对象(即想要输出Food对象的信息)又会发生什么情况呢?

可以看到,代码报错!!!这就是通配符上界的作用:帮助我们检查当前检查的类型是否是该类或者该类的子类。像这样的检查的机制,在Java中还有很多,如之前讲过的:final(指定当前方法不能被重写,重写则报错)、注解@Override(检查该方法是否已经重写,如果没有重写会报错)……

1.2 通配符下界

 语法:

<? super 下界>

<? super Integer>(代表可传入的类型可以是Integer和Integer的父类)

与上界相同可以传入自定义类型,这里的内容与通配符上界基本相同,不同的是:1、传入类型变为了当前的类或者当前类的父类。2、以通配符下界为参数的方法,是可以进行修改的。

代码:

class Plate{@Overridepublic String toString() {return "一个盘子";}
}
class Food extends Plate{public String toString() {return "一种食物";}
}
class Fruit extends Food {public String toString() {return "一种水果";}
}
class Apple extends Fruit {public String toString() {return "一个苹果";}
}
class Banana extends Fruit {public String toString() {return "一个香蕉";}
}

funSuper方法 :

    //下界一般用来写入public static void funSuper(Message<? super Fruit> temp) {System.out.println(temp.getMessage());//此时可以修改添加的是Fruit或者Fruit的子类temp.setMessage(new Banana());//向上转型temp.setMessage(new Apple());//Fruit fruit = temp.getMessage();//此时不知道是那个父类System.out.println(temp.getMessage());}

注意:此时写入是不报错的,因此泛型下界一般用来写入。 

 

测试:

  public static void main(String[] args) {Message<Food> message = new Message<>() ;message.setMessage(new Food());Message<Fruit> message1 = new Message<>() ;message1.setMessage(new Fruit());funSuper(message);funSuper(message1);}

结果: 

可以看到在funSuper方法中修改的对象信息也能被正常输出。 

相关文章:

  • 备忘录模式 (Memento Pattern)
  • 元组类型的特性与应用场景:深入理解元组在 TypeScript 中的使用
  • vue3获取麦克风权限通过websocket进行通话
  • Group By Sets语法
  • 计算机二级MS Office第九套演示文稿
  • 数据结构与算法学习笔记(Acwing提高课)----动态规划·最长上升子序列模型
  • 【Linux系统】Ext系列文件系统
  • 【黑马JavaWeb+AI知识梳理】前端Web基础01 - HTML+CSS
  • Java进阶--面向对象设计原则
  • 大规模数据同步后数据总条数对不上的系统性解决方案:从字段映射到全链路一致性保障
  • Sam算法基本原理解析
  • CPU与GPU的功能与区别解析
  • 运维面试情景题:如果有一块新的硬盘要加入机架如何配置;如果新加了一台服务器,如何配置安全措施
  • DeepSeek预训练追求极致的训练效率的做法
  • 2025.04.26-淘天春招笔试题-第三题
  • MQL5教程 06 EA开发实战
  • 【OSG学习笔记】Day 11: 文件格式与数据交换
  • Dify中的文本分词处理技术详解
  • 财务管理域——企业风控系统设计
  • Channel如何安全地尝试发送数据
  • 核电开闸!国常会核准10台新机组,拉动超2000亿投资,新项目花落谁家?
  • 俄方证实俄总统普京正在会见美特使威特科夫
  • “今日海上”对话“今日维也纳”,东西方艺术在上海碰撞
  • GDP十强省份“一季报”出炉,湖北领跑
  • 正荣地产旗下“H20正荣2”债未能于宽限期内支付分期偿付款,尚未就新兑付方案达成一致
  • 特朗普特使将赴俄见普京,俄方:美俄间谈判艰难且耗时