Java泛型完全总结 1
文章目录
一、什么是泛型?
Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常.
泛型:把类型明确的工作推迟到创建对象或调用方法时才去明确的特殊类型
参数化类型: 把类型当作是参数一样传递
<数据类型> 只能是引用类型
二、为什么需要泛型
-
我们知道,
集合可以存储任何类型的对象
,但是当把一个对象存入集合之后再次将这个对象从集合中取出来时,这个对象的数据类型就变成了Object类型
. -
所以说,如果我们
忘记了集合中元素是什么类型时,在使用这个元素时对其进行强制类型转换就很容易出错
.(ClassCastException
) -
为了解决这个问题,Java中引入了"参数化类型(parameterized type)",即泛型. 泛型可以限制集合中存入元素的类型,也可以提示编译器从集合中取出的元素是什么类型,避免了强制类型转换.
有了泛型以后:
- 代码更加简洁【不用强制转换】
- 程序更加健壮【只要编译时期没有警告,那么运行时期就不会出现ClassCastException异常】
- 可读性和稳定性【在编写集合的时候,就限定了类型】
2.1 有了泛型后使用增强for遍历集合
在创建集合的时候,我们明确了集合的类型了,所以我们可以使用增强for来遍历集合!
//创建集合对象
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
//遍历,由于明确了类型.我们可以增强for
for (String s : list) {
System.out.println(s);
}
三、泛型基础
3.1, 泛型类
特点:
- 泛型类就是把泛型定义在类上,用户
使用该类的时>>候,才把类型明确下来.
…这样的话,用户明确了什么类型,该类就代表着什么类型…用户在使用的时候就不用担心强转,运行时转换异常的问题了。
测试代码:
用户想要使用哪种类型,就在创建的时候指定类型。使用的时候,该类就会自动转换成用户想要使用的类型了。
//泛型实体类
package GenericDemoSet;
public class GenericDemo<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
测试类
package GenericDemoSet;
public class GenericDemoTest {
public static void main(String[] args) {
///范型类特点: 用户在使用类的时候才把类属性的类型明确下来;
///String类型
GenericDemo<String> gD_1 = new GenericDemo();
gD_1.setT("我是String");
System.out.println(gD_1.getT());
/// float类型
GenericDemo<Float> gD_2 = new GenericDemo();
gD_2.setT(2.2f);
System.out.println(gD_2.getT());
}
}
3.2, 泛型方法
定义泛型方法…泛型是先定义后使用的
含有泛型的方法,在调用方法的时候确定泛型的数据类型,传递什么类型的参数,泛型就是什么类型。
测试代码:
package GenericDemoSet;
public class GenericDemoTest {
泛型方法的定义(泛型放在方法的修饰符和返回值类型之间 )
// 修饰符 <T> 返回值 方法名(){}
public <T> void show(T t){
System.out.println(t);
}
public static void main(String[] args) {
GenericDemoTest gt = new GenericDemoTest();
///把方法用<T>修饰,
///用户传入什么类型,就显示什么类型
gt.show("sb");
}
}
3.3, 泛型接口
代码示例:
///接口
package GenericDemoSet;
public interface GenericeIterface <T> {
public void show(T t);
}
实现类
package GenericDemoSet;
public class GenericImp <T> implements GenericeIterface <T>{
public void show(T t){
System.out.println(t);
}
}
///测试类
package GenericDemoSet;
public class Test {
public static void main(String[] args) {
GenericeIterface<String> gI = new GenericImp<String>();
String str = "u r sb";
gI.show(str);
GenericeIterface<Integer> gI_1 = new GenericImp<>();
int s = 3;
gI_1.show(s);
}
}
四, 泛型类派生出的子类
前面我们已经定义了泛型类,泛型类是拥有泛型这个特性的类,它本质上还是一个Java类,那么它就可以被继承
那它是怎么被继承的呢??这里分两种情况
- 子类明确泛型类的类型参数变量
- 子类不明确泛型类的类型参数变量
4.1, 子类明确泛型类的类型参数变量
泛型接口
// 把泛型定义在接口上
public interface Inter<T> {
public void show(T t);
}
//实现泛型接口的类.....
/**
* 子类明确泛型类的类型参数变量:
*/
public class InterImpl implements Inter<String> {
@Override
public void show(String s) {
System.out.println(s);
}
}
4.2, 子类不明确泛型类的类型参数变量
当子类不明确泛型类的类型参数变量时,外界使用子类的时候,也需要传递类型参数变量进来,在实现类上需要定义出类型参数变量,跟泛型类使用的方法是一致的.
/**
* 子类不明确泛型类的类型参数变量:
* 实现类也要定义出<T>类型的
*
*/
public class InterImpl<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
//测试代码:
public static void main(String[] args) {
//测试第一种情况
//Inter<String> i = new InterImpl();
//i.show("hello");
//第二种情况测试
Inter<String> ii = new InterImpl<>();
ii.show("100");
}
注意:
类上声明的泛形只对非静态成员有效
五, 泛型通配符
示例代码:
六,附加知识
6.1, 泛型擦除 ☆
泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。
6.2, 兼容性
JDK5提出了泛型这个概念,但是JDK5以前是没有泛型的。也就是泛型是需要兼容JDK5以下的集合的。
当把带有泛型特性的集合赋值给老版本的集合时候,会把泛型给擦除了。
值得注意的是:它保留的就类型参数的上限。
List<String> list = new ArrayList<>();
//类型被擦除了,保留的是类型的上限,String的上限就是Object
List list1 = list;
如果我把没有类型参数的集合赋值给带有类型参数的集合赋值,这又会怎么样??
List list = new ArrayList();
List<String> list2 = list;
它也不会报错,仅仅是提示“未经检查的转换”
6.3, 泛型的应用
当我们写网页的时候,常常会有多个DAO,我们要写每次都要写好几个DAO,这样会有点麻烦。
这里写图片描述
那么我们想要的效果是什么呢??只写一个抽象DAO,别的DAO只要继承该抽象DAO,就有对应的方法了。
要实现这样的效果,肯定是要用到泛型的。因为在抽象DAO中,是不可能知道哪一个DAO会继承它自己,所以是不知道其具体的类型的。而泛型就是在创建的时候才指定其具体的类型。
抽象DAO
public abstract class BaseDao {
//模拟hibernate....
private Session session;
private Class clazz;
//哪个子类调的这个方法,得到的class就是子类处理的类型(非常重要)
public BaseDao(){
Class clazz = this.getClass(); //拿到的是子类
ParameterizedType pt = (ParameterizedType) clazz.getGenericSuperclass(); //BaseDao<Category>
clazz = (Class) pt.getActualTypeArguments()[0];
System.out.println(clazz);
}
public void add(T t){
session.save(t);
}
public T find(String id){
return (T) session.get(clazz, id);
}
public void update(T t){
session.update(t);
}
public void delete(String id){
T t = (T) session.get(clazz, id);
session.delete(t);
}
}
继承抽象DAO,该实现类就有对应的增删改查的方法了。
//CategoryDao
public class CategoryDao extends BaseDao<Category> {
}
//BookDao
public class BookDao extends BaseDao<Book> {
}
参考资料:
https://www.zhihu.com/question/20400700/answer/117464182
https://segmentfault.com/a/1190000014120746
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)