网站首页 > java教程 正文
1、泛型介绍
1.1 泛型引入背景
早期没有泛型概念时,创建集合存入的元素类型都当成Object,使用的时候必须清楚集合中每个元素的类型,否则当我们对元素进行操作时容易出错,例如转换元素的类型可能出现ClassCastException异常。
示例代码:
如下代码,for循环循环到第二个元素的时候,由于元素原始类型为int,而代码强制转换为String,出现了ClassCastException。
public class Main {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("ggy"); //存入String类型
arrayList.add(123); //存入int类型
arrayList.add(true); //存入bool类型
//使用集合时,我们并清楚集合中每个元素类型,假设都当成String进行处理
for (int i = 0; i < arrayList.size(); i++) {
String s = (String)arrayList.get(i);//此次进行强转
System.out.println(s);
}
}
}
针对以上问题,Jdk5开始引入泛型,我们来看下使用泛型,如何处理上述的问题。
如下图,当我们对集合设置了泛型后,集合添加了不同类型的元素时,代码处直接标红,编译不同。
1.2 泛型的概念
Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构。泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。
1.3 泛型的作用
- 提供了编译时类型安全监测机制,保证数据类型安全
- 消除了强制类型的转换
2、泛型类
2.1 泛型类定义语法
泛型标识可以作为成员变量的类型,和成员方法的返回值类型及参数类型
class 类名称 <泛型标识,泛型标识,…> {
private 泛型标识 变量名;
.....
}
常用的泛型标识:T、E、K、V
示例代码
public class Generice <T>{ //T为泛型类的泛型标识
private T key; //成员变量的参数类型被指定为T泛型标识
public Generice(T key) { //泛型标识作为构造器的参数类型
this.key = key;
}
public T getKey() { //泛型标识作为成员方法的返回值类型
return key;
}
public void setKey(T key) { //泛型标识作为成员方法的参数类型
this.key = key;
}
}
2.2 泛型类使用
2.2.1 语法
Java1.7以前
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
Java1.7以后
后面的<>中的类型可以省略
类名<具体的数据类型> 对象名 = new 类名<>();
示例代码:
public class Animal <T>{
private T name;
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
}
class Main{
public static void main(String[] args) {
Animal<String> animal = new Animal<>(); //泛型类使用
animal.setName("Ggy");
System.out.println(animal.getName());
}
}
2.2.2 泛型类注意事项
1)泛型类,如果没有指定具体的数据类型,此时操作类型是Object
如下第一张图,没有指定类型时,idea显示形参化类的原始使用,第二张图指定了String没有显示这个信息。
2)泛型的类型参数只能是类类型,不能是基本数据类型
3)泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型
可以结合后面的泛型擦除理解,泛型擦除之后 Animal< Integer> 和Animal< String>都是Animal类。
2.3 从泛型类派生子类
1)子类也是泛型类,子类和父类的泛型类型要一致
语法:class ChildGeneric<T> extends Generic<T>
2)子类不是泛型类,父类要明确泛型的数据类型
语法:class ChildGeneric extends Generic<String>
3、泛型接口
3.1 泛型接口定义语法
泛型标识可以作为成员方法的返回值类型及参数类型
interface 接口名称 <泛型标识,泛型标识,…> {
泛型标识 方法名();
.....
}
常用的泛型标识:T、E、K、V
示例代码:
public interface Animal<T> {
void getName(T t);
T getName();
}
3.2 泛型接口的使用
3.2.1 语法
接口名<具体数据类型> 变量名 = new 实现类名<>();
示例代码:
public interface Animal<T> {
T getName();
void setName(T name);
}
class Cat<T> implements Animal<T>{
private T name;
@Override
public T getName() {
return name;
}
@Override
public void setName(T name) {
this.name = name;
}
}
class Main{
public static void main(String[] args) {
//使用泛型接口
Animal<String> cat1 = new Cat<>();
Cat<String> cat2 = new Cat<>();
}
}
3.2.2 使用泛型接口注意点
1)实现类也是泛型类,实现类和接口的泛型类型要一致
2)实现类也是泛型类,实现类和接口的泛型类型要一致
4、泛型方法
4.1 定义
泛型类,是在实例化类的时候指明泛型的具体类型,泛型方法,是在调用方法的时候指明泛型的具体类型。
4.2 泛型方法使用
语法:
修饰符 <T,E, ...> 返回值类型 方法名(形参列表) {
方法体...
}
- public与返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
- 只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
- <T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
- 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
4.3 泛型类和内部泛型方法的泛型标识的理解
【注】泛型接口\类的泛型标识,如果与接口\类内部的泛型方法的泛型标识同名,这两个泛型标识是不能等同的。
4.4 泛型方法与可变参数
按如下方式可传递多个参数
public <E> void print(E... e){
for (E e1 : e) {
System.out.println(e);
}
}
示例代码
public interface Generic {
public <E> List<E> getList(E...e);
}
class SubGeneric implements Generic{
@Override
public <E> List<E> getList(E... e) {
ArrayList<E> list = new ArrayList<>();
Arrays.stream(e).forEach(a->{
list.add(a);
});
return list;
}
}
class Main{
public static void main(String[] args) {
SubGeneric subGeneric = new SubGeneric();
//传递多个参数
List<? extends Serializable> list = subGeneric.getList("ggy", 123, "czp", true);
System.out.println(list);
}
}
4.5 泛型方法总结
- 泛型方法能使方法独立于类而产生变化
- 如果static方法要使用泛型能力,就必须使其成为泛型方法
5、类型通配符
5.1 什么事类型通配符
类型通配符一般是使用"?"代替具体的类型实参。类型通配符是类型实参,而不是类型形参。
5.2 类型通配符上限
语法
类/接口<? extends 实参类型>
要求该泛型的类型,只能是实参类型,或实参类型的子类类型。
示例代码:
5.3 类型通配符下限
语法
类/接口<? super实参类型>
要求该泛型的类型,只能是实参类型,或实参类型的父类类型。
示例代码:
5.4 类型通配符使用注意
只能用在方法的返回值或者方法的参数列表中,不能用在类上。
用在类上报错:
用在方法上:
5.5 泛型实参后误解点
下述代码我通常会误解认为应该编译通过,但是编辑器显示编译出现问题,这是因为当泛型指定了实参后,传入的参数的类型应该和指定的实参类型一致,如下printCollection方法的参数的泛型指定为Object,那么我们调用这个方法时,传入的参数的泛型也应该是Object.
那么上面的案例能否优化呢?可以通过“?”来实现,“?”代表任意的实参,所以编译可以通过。
或者我们通过泛型类实现也可以
或者通过泛型方法实现
6、类型擦除
6.1 概念
泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是,泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为--类型擦除。
6.2 类型擦除种类
6.2.1 无限制类型擦除
6.2.2 有限制类型擦除
6.2.3 桥接方法
6.2.4 擦除方法中类型定义的参数
猜你喜欢
- 2024-10-16 Java泛型详解(完整版)必看(java 泛型 t)
- 2024-10-16 Java泛型最全总结(java泛型的实现原理)
- 2024-10-16 Java高级面试:Java的泛型实现机制是怎么样的?
- 2024-10-16 深入理解 Java 泛型(java泛型的实现原理)
- 2024-10-16 关于Java中的泛型使用你不知道的那些事?
- 2024-10-16 Java泛型机制详解;这些你都知道吗?
- 2024-10-16 Java中的泛型是什么?(java里面的泛型)
- 2024-10-16 学习廖雪峰的JAVA教程---泛型(使用泛型)
- 2024-10-16 Java泛型的好处及实现原理详解(java里泛型)
- 2024-10-16 java中的泛型是什么(java中泛型的理解)
你 发表评论:
欢迎- 最近发表
-
- Java常量定义防暴指南:从"杀马特"到"高富帅"的华丽转身
- Java接口设计原则与实践:优雅编程的艺术
- java 包管理、访问修饰符、static/final关键字
- Java工程师的代码规范与最佳实践:优雅代码的艺术
- 编写一个java程序(编写一个Java程序计算并输出1到n的阶乘)
- Mycat的搭建以及配置与启动(mycat部署)
- Weblogic 安装 -“不是有效的 JDK Java 主目录”解决办法
- SpringBoot打包部署解析:jar包的生成和结构
- 《Servlet》第05节:创建第一个Servlet程序(HelloSevlet)
- 你认为最简单的单例模式,东西还挺多
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)