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

泛型进阶之通配符

通配符

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

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方法中修改的对象信息也能被正常输出。 

http://www.xdnf.cn/news/2360.html

相关文章:

  • import tree # pip install dm_tree ModuleNotFoundError: No module named ‘tree‘
  • 如何导出1寸分辨率为300及以上的照片?
  • 常见cmd命令
  • 基于PyTorch的图像识别主要依赖于深度学习模型(尤其是卷积神经网络,CNN)对图像特征进行自动学习和分类
  • tigase源码学习杂记-IO处理的线程模型
  • Python-MCPServerStdio开发
  • python输出
  • 防火墙规则配置错误导致的网络问题排查
  • Tauri v2 配置全解析(完整版)
  • Eigen线性代数求解器(分解类)
  • 内存大冒险
  • ai与望闻问切
  • 2025最新Facefusion3.1.2使用Docker部署,保姆级教程,无需配置环境
  • C语言输入输出完全指南:从基础到文件操作
  • MCP 协议解读:STDIO 高效通信与 JSON-RPC 实战
  • Java大师成长计划之第4天:Java中的泛型
  • Android Gradle插件开发
  • AI Agent: MCP和AI Agent的联系
  • 线程池(二):深入剖析synchronized关键字的底层原理
  • 1位的推理框架bitnet.cpp
  • MySQL之视图
  • DIFY 浅尝 - Dify + Ollama 抓取BBC新闻
  • babel核心知识点
  • ARM架构的微控制器总线矩阵
  • 【Java面试笔记:进阶】21.Java并发类库提供的线程池有哪几种? 分别有什么特点?
  • Java 实现目录递归拷贝
  • Agent2Agent
  • MyBatis 插件开发的完整详细例子
  • Python 实现将图像发送到指定的 API 进行推理
  • docker配置mysql遇到的问题:网络连接超时、启动mysql失败、navicat无法远程连接mysql