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

Java中的泛型 Generics

一.初识泛型

泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。由尖括号(<>)分隔的类型参数部分跟在类名后面。它指定类型参数(也称为类型变量)T1,T2,...和 Tn。

//语法格式
class name<T1, T2, ..., Tn> { /* ... */ }
public class Printer<T> { //占位符T代表这个类可以传入任何类型的参数 //内部也可以传入多个参数<T,K>     T content;                                   public Printer(T content) {                  this.content = content;                  }                                            public T getContent() {                      return content;                          }                                            public void setContent(T content) {          this.content = content;                  }                                            public void print() {                        System.out.println(content);             }                                            
}                                                
//调用函数
public class Main {public static void main(String[] args) {//使用Integer因为Java泛型底层实现是类型擦除,只有对象才容易在运行时擦除Printer<Integer> p=new Printer<>(123);p.print();}
}

泛式的优点:

  • 编译时的强类型检查

      泛型要求在声明时指定实际数据类型,Java 编译器在编译时会对泛型代码做强类型检查,而以  前没有泛式的时候是在运行时进行检查的。

  • 避免了类型转换
  • 泛型编程可以实现通用算法

二.有界限的泛型

在实际做项目时,类型参数不用满足任何类型,因此我们对于这个类型参数可以做一些约束

  • 比如传入的参数必须是某个类型的子类型就在<>里用到extends
public class Printer<T extends Car> {}//代表T必须是Car的子类型(eg:volvo)
  • 当然我们也可以用接口,此时仍然用extends,而不是implments
public class Printer<T extends Run> {}//代表T必须是Run接口的实现
  • 也可以把接口和类同时写在一起,此时的class必须写在interface前面,否则会报错
//注意:extends 关键字后面的第一个类型参数可以是类或接口,其他类型参数只能是接口。
public class Printer<T extends Car & Run> {}
  • 相反,如果必须是父类型的话就用super
public class Printer<T super Car> {}//T是Car的父类或者Car本身

三.泛型方法Generic Method

是否拥有泛型方法,与其所在的类是否是泛型没有关系。

情景:假设我们现在有这样一个需求,需要写一个print方法去打印任意的变量

    private  static  void print(T content){System.out.println(content);//此时会报错}

当我们写上述的方法时,会报错。那是因为T其实只不过是一个占位符而已,它在Java里面并不是一种明确的类型。那我们又该如何告诉Java我们使用的是泛型呢

解决方法: 在返回值类型前加一个<>,再把T包含在里面

    private static <T> void print(T content){System.out.println(content);//此时不会报错了}

四.类型通配符 ?

那当我们碰到的类型参数是List的情况又是怎么样的呢?

import java.util.ArrayList;
import java.util.List;public class Main {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("hello");list.add("world");print(list);//此时会报错,哪怕String继承Object,但是此时List<String>这个整体并不是List<Object>子类//正是由于泛型时基于类型擦除实现的,所以,泛型类型无法向上转型。//Integer 继承了 Object;ArrayList 继承了 List;但是 List<Interger> 却并非继承了 List<Object>。//这是因为,泛型类并没有自己独有的 Class 类对象。比如:并不存在 List<Object>.class 或是 List<Interger>.class,Java 编译器会将二者都视为 List.class}private static void print(List<Object> content){System.out.println(content);//此时会报错}
}

此时可以用通配符来解决这个问题

    private static void print(List<?> content){System.out.println(content);}
 //用以前的老方法<T>也可以private static<T> void print(List<T> content){System.out.println(content);}

五.类型擦除

Java 泛型是使用类型擦除来实现的,使用泛型时,任何具体的类型信息都被擦除了

那么,类型擦除做了什么呢?它做了以下工作:

  • 把泛型中的所有类型参数替换为 Object,如果指定类型边界,则使用类型边界来替换。因此,生成的字节码仅包含普通的类,接口和方法。

  • 擦除出现的类型声明即去掉 <> 的内容。比如 T get() 方法声明就变成了 Object get() ;List<String> 就变成了 List。如有必要,插入类型转换以保持类型安全。

  • 生成桥接方法以保留扩展泛型类型中的多态性。类型擦除确保不为参数化类型创建新类;因此,泛型不会产生运行时开销。

//编译之后集合的泛型是去泛型化的
//Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了
//我们可以通过方法的反射来操作,绕过编译
public class GenericsErasureTypeDemo {public static void main(String[] args) {List<Object> list1 = new ArrayList<Object>();List<String> list2 = new ArrayList<String>();System.out.println(list1.getClass());System.out.println(list2.getClass());}
}
// Output:
// class java.util.ArrayList
// class java.util.ArrayList

示例说明:

上面的例子中,虽然指定了不同的类型参数,但是 list1 和 list2 的类信息却是一样的。

这是因为:使用泛型时,任何具体的类型信息都被擦除了。这意味着:ArrayList<Object> 和 ArrayList<String> 在运行时,JVM 将它们视为同一类型。

六.桥接方法

前面泛型的类型擦除我们提到了桥接方法,那么什么是桥接方法呢?

    子类在重写父类的方法时,必须保证和父类有相同的方法名称,参数列表和返回类型,那当泛型父类和泛型接口的泛型参数被擦除了,那子类的重写方法岂不是不满足重写规则了,如下图所示,显然违背了重写规则,但程序为什么还是能正常编译呢?

    所以Java为了解决类型擦除和重写的冲突,Java会在编译阶段,编译器为这些继承(实现)泛型类(接口)的子类创建一个合成方法,用来保持扩展泛型类型中的多态性,个被创建出来的方法,被我们称为桥接方法。

    桥接方法,顾名思义,它是父类和子类之间的一座桥梁,它是实际上的重写了父类的方法,在方法内部委托了子类的原始方法,这样就巧妙的绕过了类型擦除带来的影响

http://www.dtcms.com/a/460780.html

相关文章:

  • [linux仓库]信号产生[进程信号·贰]
  • 网站内部链接优化网红营销李佳琦案例分析
  • 有哪些网站设计比较好的公司wordpress右上角登录
  • 在Python中加载.pkl文件
  • HarmonyOS大厂面试总结大全二
  • python 字符串压缩(字符串-中等)含源码(九)
  • 如何自己设计一个网页宁波seo推广哪家好
  • 【STM32项目开源】STM32单片机智能温室大棚控制系统
  • 车牌 OCR 识别:国庆高速免费通行的 “隐形引擎”
  • wpf之TabControl
  • WPF应用最小化到系统托盘
  • 使用平行型子环腔的 23 KHz 线宽 1064 nm SOA 光纤激光器
  • 保定企业建站程序wordpress数据都被存在哪
  • 深圳做网站做app少儿戏曲知识 网站建设
  • SymPy 符号计算:从基础到高级的完整指南
  • 成免费crm推广网站黄石网站建设黄石
  • HTB Monitored writeup(nagios api v1 login)
  • 开源 C++ QT QML 开发(十三)多线程
  • 企业如何建设网站,企业搭建网站的流程
  • HarmonyOS SaveButton深度解析:安全便捷的媒体资源保存方案
  • 如何用开源外卖系统源码打造私域O2O生态?技术+运营双轮驱动
  • {title:敏捷开发实战如何利用Scrum框架在30天内交付高质量软件}
  • 浏览器端音视频处理新选择:Mediabunny 让 Web 媒体开发飞起来
  • iOS 26 能耗监测全景,Adaptive Power、新电池视图
  • 微软警告:攻击者将Microsoft Teams武器化用于勒索软件、间谍活动及社会工程攻击
  • QT MVC中View的特点及使用注意事项
  • WAF防护的性能优化策略
  • MyBatis-Spring集成完全指南
  • 如何知道自己的台式电脑的所有硬件信息
  • 门户网站 商城系统免费的网络推广