【JavaSE】泛型

8 基本概念和原理

一个简单泛型类

基本概念

泛型就是类型参数化,处理的数据类型是不固定的,而是可以作为参数传入

  • Object<T>
  • 其中的T就是传递的实际参数类型。
  • 参数类型可以有多个,如Object<U,V>多个类型之间用逗号分隔

基本原理

Java有Java编译器和Java虚拟机,编译器将Java源代码转换为.class文件,虚拟机加载并运行.class文件。
对于泛型类,Java编译器会将泛型代码转换为普通的非泛型代码,将类型参数T擦除,替换为Object,插入必要的强制类型转换。
强调:Java泛型是通过擦除实现的,T会被替换为Object,程序运行时,并不知道泛型的实际类型参数。

泛型的好处

  • 更好的安全性
  • 更好的可读性

容器类

泛型方法

public static <T> int indexOf(T[] arr, T elm){
	//具体方法
}

可以通过T,使用

  • indexOf(new String[]{"a","b"});
  • indexOf(new int[]{1,2});

泛型接口

如 Comparable、Comparator

类型参数的限定

  • 参数必须为给定的上届类型或其子类型,这个限定是通过extends关键字来表示的。
  • 这个上界可以是某个具体的类或者某个具体的接口,也可以是其他的类型参数。

上界为某个具体的类

public class NumberPair<U extends Number, V extends Number> extends Pair<U, V>{
	public NumberPair(U first, V second){
		super(first, second);
	}
}

上界为某个接口

public static <T entends Comparable>T max(T[] arr){
	T max = arr[0]
	//函数业务逻辑
}

此时,Java会给一个警告,因为Comparable是一个泛型接口它也需要一个类型参数

public static <T entends Comparable<T>>T max(T[] arr){

	//函数业务逻辑
}

<T extends Comparable>形式称为地柜类型限制,解读:

  • T表示一种数据类型,必须实现Comparable接口,且必须可以与相同类型的元素进行比较。

上界为其他类型参数

Java支持一个类型参数以另一个类型参作为上界。

public void addAll(DynamicArray<E> c){
	for(int i=0; i<c.size; i++){
		add(c.get(i));
	}	
}

但存在局限性

DynamicArray<Number> numbers = new DynamicArray<>();
DynamicArray<Integer> numbers = new DynamicArray<>();
ints.add(100);
inst.add(34);
numbers.addAll(ints);//会提示编译错误

虽然Integer是Number的子类,但DynamicArray并不是DynamicArray的子类,DynamicArray的对象也不能赋值给DynamicArray的变量。

可以使用类型限定来解决:

public <T extends E> void addAll(DynamicArray<T> c){
	//业务逻辑
}

小结

泛型是计算机程序中一种重要的思维方式,它将数据结构和算法与数据类型相分离,使得同一套数据结构和算法能够应用于各种数据类型,而且可以保证类型安全,提高可读性。
在Java中,泛型是通过类型擦除来实现的,它是Java编译器的概念,Java虚拟机运行时对泛型基本一无所知。

解析通配符

更简洁的参数类型限定

为了将Integer对象添加到Number容器中,我们的类型参数使用了其他类型参数作为上界。
写法繁琐,它可以替换位:?

public void addAll(DynamicArray<? extends E> c){

}

这种方法没有定义类型参数,c的类型是DynamicArray<? extends E>,?表示通配符,<? extends E>表示有限定通配符,匹配E或E的某个子类型,具体什么子类型是未知的。

和 <? extends E>有什么关系?

  • 用于定义类型参数,它声明了一个类型参数T,可放在泛型类定义中类名后面、泛型方法返回值前面
  • <? extends E> 用于实例化类型参数,它用于实例化泛型变量中的类型参数,只是这个具体类型是未知的,只知道它是E或E的某个子类型。
  • 虽然有所不同,但两种写法经常可以达成相同目标:
public void addAll(DynamicArray<? extends E> c)
public <T extends E> void addAll(DynamicArray<T> c)

理解通配符

除了有限定通配符,还有一种通配符,形成DynamicArray<?>,称为无限定通配符
通配符形式更为简洁,但是存在一个重要限制,只能读,不能写

  • 借助待类型参数的泛型方法,可以解决不能写入的问题。
private static <T> void swapIntegerNal(DynamicArray<T> arr, int i, int j){
	//写入
}
public static void swap(DynamticArry<?> arr, int i, intj){
	swapIntegerNal(arr, i, j);
}

除了这种需要写的场合,如果参数类型之间有依赖关系,也只能用类型参数。比如将src容器中的内容复制到dest中:

public static <D, S extends D> void copy(DynamicArray<D> dest, DynamicArray<S> src){
	for(int i = 0; i < src.size(); i++){
		dest.add(src.get(i))
	}
}

S和D有依赖关系,要么相同,要么S是D的子类,否则类型不兼容,有编译错误。
上面的声明可以使用通配符简化,两个参数可以简化为一个。

public static <D> void copy(DynamicArray<D> dest, DynamicArray<? extends D> src){
	//业务逻辑
}

注:如果返回值依赖于类型参数,也不能用通配符。

总结:

  • 通配符形式都可以用类型参数的形式来替代,通配符能做的,用类型参数都能做
  • 通配符形式可以减少类型参数,形式上往往更为简单,可读性也更好,所以,能用通配符的就用通配符。
  • 如果类型参数之间有依赖关系,或者返回值依赖类型参数,或者需要写操作,则只能用类型参数
  • 通配符形式和类型参数往往配合使用,比如,上面的copy方法,定义必要的类型参数,使用通配符表达依赖,并接受更广泛的数据类型。

超类型通配符。

还有一种通配符,与<? extends E>相反, 它的形式为<? super E>,称为超类型通配符

通配符比较

三种通配符形式:<?>、<? extends E>、<? super E>

  • 它们的目的都是为了使方法接口更为灵活,可以接受更为广泛的类型。
  • <? super E>用于灵活写入或比较,使得对象可以写入父类型的容器,使得父类型的比较方法可以应用于子类对象,它不能被类型参数形式替代。
  • <?>和<? extends E>用于灵活读取,使得方法可以读取E或E的任意子类型的容器对象,它们可以用类型参数的形式替代,但通配符形式更为简介。

细节和局限性

使用泛型类、方法、接口

定义泛型类、方法、接口

泛型与数组

泛型与数组的关系

  • Java不支持创建泛型数组
  • 如果要存放泛型对象,可以使用原始类型的数组,或者使用泛型容器
  • 泛型容器内部使用Object数组,如果要转换泛型容器为对应类型的数组,需要使用反射。

总结

泛型的局限性:

  • 不能使用基本类型
  • 没有运行时类型信息
  • 类型擦除会引发一些冲突
  • 不能通过类型参数创建对象
  • 不能用于静态变量
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ava实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),可运行高分资源 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值