从0到掌握Java泛型有这一篇博客就够了
1.泛型的概述
2.泛型类
2.泛型接口
4.泛型方法
5.泛型通配符
6.泛型数组
1.泛型的概述
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用
来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
我们举个例子:
package untl2;
import java.util.ArrayList;
import java.util.List;
public class MyType {public static void main(String[] args) {List arrayList = new ArrayList();arrayList.add("aaaa");arrayList.add(100);for(int i = 0; i< arrayList.size();i++) {String item = (String) arrayList.get(i);System.out.println(item);}}
}运行结果:
程序崩溃并且抛出ClassCastException异常
那么为啥会抛出异常呢:
这个例子就是典型的编译时正常,运行出错
ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。
那么我们用泛型如何解决这个问题呢,看下面代码:
package untl2;
import java.util.ArrayList;
import java.util.List;
public class MyType {public static void main(String[] args) {List<String> arrayList = new ArrayList<String>();arrayList.add("aaaa");arrayList.add(100);for(int i = 0; i< arrayList.size();i++) {String item = arrayList.get(i);System.out.println(item);}}
}
此代码编译不通过,由于泛型已经规定arrayList是String类型,但是100是int类型,所以编译直接报错,这个时候就体现出泛型的优势了,而且仅仅就在List后边加了<String>
就能为所欲为了
那么对于这两段代码时候给出总结了泛型的两大优点:
1.将运行时期的ClassCastException,转移到了编译时期变成了编译失败
2.避免了类型强转的麻烦。
再看一个例子:
package untl2;
import java.util.ArrayList;
public class MyType {public static void main(String[] args) {ArrayList<String> arrayList1=new ArrayList();ArrayList<Integer> arrayList2=new ArrayList();Class cla1=arrayList1.getClass();Class cla2=arrayList2.getClass();if(cla1==cla2){System.out.println("运行后会进行去泛型的操作所以两者相等");}elseSystem.out.println("运行后不会进行去泛型的操作");}
}
运行结果:
运行时会进行去泛型的操作所以两者相等
通过上面的例子可以证明,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。泛型的本质就是利用编译器实现的Java语法糖,编译器将java文件转换为class文件前,会进行泛型擦除,所以在反编译的class文件中,是看不到泛型声明的
是时候搬出我泛型的祖传定义和概述了:
泛型,即“
参数化类型
”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
2.泛型类
2.1.泛型类的定义格式:
class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{private 泛型标识 /*(成员变量类型)*/ value; .....}
}
2.2.泛型类的具体实例:
我们可以看一下我们API中的ArrayList集合源码:
class ArrayList<E>{ public boolean add(E e){ }public E get(int index){ }....
}
当然,我们也可以自定义例如:
但是要注意以下两点:
1.泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
2.传入的实参类型需与泛型的类型参数类型相同
package untl2;
public class MyType<T> {private T key;public MyType(T t){this.key=t;}public T getkey(){return this.key;}public static void main(String[] args) {String str="123";MyType<String> myType=new MyType(str);//这里<String>可以不写会自动进行检测}}
定义的泛型是不需要传入具体的实参的,什么意思呢,还是上边这个例子, MyType<String> myType=new MyType(str);
我们指定了泛型为String,当然我们也能不指定泛型类型,随便在构造器中传入任何类型的数据,编译器会自动检测
3.泛型接口
3.1.泛型接口的定义格式:
public interface Generator<T> {public T next();
}
3.2.泛型接口的实现类未传入泛型实参:
例如:
package untl2;
public interface MyType<T> {public T fly();
}
class person implements MyType{}
那么我们知道实现接口肯定要重写里边的抽象方法,但是接口的泛型你不明确,所以重写方法的返回值类型也不明确,所以我们可以使用Object
作为方法的反回值来解决:
package untl2;
public interface MyType<T> {public T fly();
}
class person implements MyType{public Object fly(){System.out.println("让我飞");return null;}public static void main(String[] args) {person p=new person();p.fly();}
}
运行结果:
让我飞
3.3.泛型接口的实现类传入泛型实参:
分为两种情况,
(1)第一种是实现接口不带泛型
package untl2;
public interface MyType<T> {public T fly();
}
class person<E> implements MyType{public E fly(){System.out.println("让我飞");return null;}public static void main(String[] args) {person<String> p=new person();p.fly();}
}
运行结果:
让我飞
(2)第二种是两者都带泛型:
package untl2;
public interface MyType<T> {public T fly();
}
class person<T> implements MyType<T>{public T fly(){System.out.println("让我飞");return null;}public static void main(String[] args) {person<String> p=new person();p.fly();}
}
运行结果:
我要飞
我们必须注意 实现接口所带泛型必须要抽象方法的泛型参数一样
要不然:
package untl2;
public interface MyType<T> {public T fly();
}
class person<E> implements MyType<T>{public E fly(){System.out.println("让我飞");return null;}public static void main(String[] args) {person<String> p=new person();p.fly();}
}
编译报错
3.4.泛型接口传入实参:
package untl2;
public interface MyType<T> {public T fly();
}
class person implements MyType<String>{public String fly(){System.out.println("让我飞");return null;}public static void main(String[] args) {person p=new person();p.fly();}
}
4.泛型方法
泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型
4.1.泛型方法的定义格式:
修饰符 <泛型> 返回值类型(可以使用泛型) 方法名(参数列表(可以使用泛型)){方法体;}
4.2.泛型方法的简单使用及代码分析:
package untl2;public class MyThroad<T>{private T key;public MyThroad(T t){this.key=t;}public T getKey(){return key;}public <E> E func(T t){System.out.println("我很开心");return null;}
// public <E> E vunc(MyThroad<K> k )
// {//
// }public <E,K> E vunc(MyThroad<K> k ){return null;}public static void main(String[] args) {}}
分析之前请先好好看看这一句话:
泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型
首先getkey()
不是一个泛型方法
1.从泛型确定类型的时间:这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。在你实例化对象的时候就会指定T的类型,并不是调用的时候确定的
2.格式上:与泛型方法的定义格式不一致
func
方法是一个泛型方法
被注释的vunc()方法
虽然我们声明了,也表明了这是一个可以处理泛型的类型的泛型方法。
但是只声明了泛型类型T,并未声明泛型类型K,因此编译器并不知道该如何处理E这个类型。
注:在泛型方法里边定义的泛型要不是在调用方法前就确定了类型,要不就要在尖括号里边声明
4.3.泛型类中的泛型方法:
这里用内部类的例子:
package untl;
public class GenericFruit {class Fruit{@Overridepublic String toString() {return "fruit";}}class Apple extends Fruit{@Overridepublic String toString() {return "apple";}}class Person{@Overridepublic String toString() {return "Person";}}class GenerateTest<T>{public void show_1(T t){System.out.println(t.toString());}//在泛型类中声明了一个泛型方法,使用泛型E,///这种泛型E可以为任意类型。可以类型与T相同,也可以不同。//由于泛型方法在声明的时候会声明泛型<E>,//因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。public <E> void show_3(E t){System.out.println(t.toString());}//在泛型类中声明了一个泛型方法,使用泛型T,//注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。public <T> void show_2(T t){System.out.println(t.toString());}}public static void main(String[] args) {GenericFruit frult=new GenericFruit();Apple apple =frult.new Apple();Person person = frult.new Person();GenerateTest<Fruit> generateTest = frult.new GenerateTest<Fruit>();generateTest.show_1(apple);//generateTest.show_1(person);//使用这两个方法都可以成功generateTest.show_2(apple);generateTest.show_2(person);generateTest.show_3(apple);generateTest.show_3(person);}
}
运行结果:
apple
apple
Person
apple
Person
4.3.泛型方法与可变参数:
package untl;
public class GenericFruit {public <T> void printf(T...d){for (T t:d){System.out.println(t);}}public static void main(String[] args) {GenericFruit p=new GenericFruit();p.printf("张三",123,88.88,true);}}
运行结果:
张三
123
88.88
true
4.4.静态方法与泛型:
如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。
如果静态方法使用泛型,但又不是泛型方法:
编译就会不通过
但是,声明成泛型方法就会解决这个问题
5.泛型通配符
5.1.泛型通配符’?’
?:代表任意的数据类型
使用方式:
1.不能创建对象使用
2.只能作为方法使用
看下面一段代码:
明显的list2不是Interger类型,所以编译会报错,但是如果我就想使用print方法遍历任意泛型的ArrayList集合,这个时候就可以使用我们的通配符
package untl;
import java.util.ArrayList;
import java.util.Iterator;
public class GenericFruit {public static void print(ArrayList<?> list){Iterator<?> it=list.iterator();while (it.hasNext()){Object obj=it.next();System.out.println(obj);}}public static void main(String[] args) {ArrayList<Integer> list1=new ArrayList<>();list1.add(1);list1.add(2);ArrayList<String> list2=new ArrayList<>();list2.add("aaa");list2.add("bbb");print(list1);print(list2);}
}
运行结果:
1
2
aaa
bbb
一定注意通配符必须在定义的时候不能用泛型通配符如:
ArrayList<?> list2=new ArrayList<?>()
;就会报错,但是作为参数传递的时候可以用,就比如以上的例子
5.2.泛型通配符的高级使用—受限泛型:
在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。
泛型的上下限:
1.上限格式:
类型名称 <? extends 类> 对象名称
,意义:只能接受该类型及其子类
2.下限格式:类型名称 <? super 类> 对象名称
,意义:只能接受此类型及其父类、
看下面这个例子:
首先我们知道Number
类是Integer的父类
package untl;
import java.util.ArrayList;
import java.util.Collection;
public class GenericFruit{public static void main(String[] args) {Collection<Integer> list1 = new ArrayList<Integer>();Collection<String> list2 = new ArrayList<String>();Collection<Number> list3 = new ArrayList<Number>();Collection<Object> list4 = new ArrayList<Object>();getElement1(list1);getElement1(list2);//报错getElement1(list3);getElement1(list4);//报错getElement2(list1);//报错getElement2(list2);//报错getElement2(list3);getElement2(list4);}// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类public static void getElement1(Collection<? extends Number> coll){}// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类public static void getElement2(Collection<? super Number> coll){}
}
6.泛型数组
在java中是”不能创建一个确切的泛型类型的数组”的。
也就是说下面这段代码是报错的:
List<String>[] ls = new ArrayList<String>[10];
这样也是报错的:
List ls = new ArrayList<String>[10];
但是而使用通配符创建泛型数组是可以的,如下面这个例子:
List<?>[] ls = new ArrayList<?>[10];
这样也是可以的:
List<String>[] ls = new ArrayList[10];
从0到掌握Java泛型有这一篇博客就够了相关推荐
- java实现阅读量统计_博客中的阅读量是如何设计的?
在博客园中,一篇博客的底部,通常有该篇博客的阅读量的统计.当浏览器端没发起一个请求的时候,它通过相应的逻辑判断,如果符合要求,则给阅读量加一.所以,有了如下代码: package test; impo ...
- 第一篇 博客:java从数据库读取数据,并写入到excel表格中
今天,组长分配了查询数据库记录,并把这些记录写入到excel表格中,以前没有尝试过,借鉴了一些别人的代码,最终实现了功能,写一篇博客,总结一下这个过程. 1.准备需要用到的jar包 1.jxl.jar ...
- 号称史上最牛逼的几篇博客整理(python+java+js等)
号称史上最牛逼的几篇博客这个说法也许夸张了点,但总体来说楼主整理的博客都是楼主幸苦之作,并且都是围绕去转盘网展开,这些博客几乎透漏了该站的所有技术关键,细节,甚至所有核心代码,我个人认为作为一个有艺术 ...
- 学生优惠0元免费开始搭建属于自己的个人博客阿里云
学生优惠-长期有效|0元免费开始搭建属于自己的个人博客|阿里云 文章目录 学生优惠-长期有效|0元免费开始搭建属于自己的个人博客|阿里云 阿里云免费活动 个人博客快速搭建 端口配置 宝塔面板安装 阿里 ...
- JAVA开源学习者_新浪博客
置顶: 导读:zhuoqun原来是雅虎(中国)和淘宝的一名技术工程师,目前正在一家公司做前端工程师.他最近的一篇博客<开发与研发>引发了业界的讨论和思考.在他看来,"程序开发大概 ...
- Java集合和泛型练习及面试题——博客园:师妹开讲啦
给定一段JAVA代码如下:要打印出list中存储的内容,以下语句正确的是( B ) ArrayList list = new ArrayList( ) list.add("a") ...
- 手把手教你做一个新浪博客发布软件JAVA版本(5)--打开博客发布页面并解析博客内容
前言:很多人用新浪博客引流,但是以前可以用api发布,但是现在已经行不通了,市面上也有诸如新浪博客批量发布软件啦,新浪博客批量发帖啦,新浪博客发布软件啊等等的各种工具,但是小心中枪,一 ...
- 解读java面试_解读王垠博客“一道 Java 面试题”
偶然拜读IT界知名大佬王垠老师的博客,发现一个有意思的题目: 1 //这段代码里面到底哪一行错了?为什么?2 //原文:http://www.yinwang.org/blog-cn/2020/02/1 ...
- C# ASP.NET程序员整合Java门户单点登录PHPwind论坛博客软件集成项目经验总结
为什么80%的码农都做不了架构师?>>> 有人曾问,如何正确估算项目周期?你是天天写C#程序的程序员,若做了一个 PHPwind 的单点登录,那估计需要几天时间? 客户只提了一 ...
最新文章
- 【camera】4.图像的颜色空间
- 语音控制 python_用Python编程实现语音控制电脑
- java中queue排序_Java中常见的排序算法有哪些?---选择排序
- 如何站在期刊角度上看论文写作
- 五分钟精通Oracle Hints
- ansible常用模块入门
- Intellij IDEA 通过数据库表逆向生成带注释的实体类文件超级详细步骤,附详细解决方案
- vb杨辉三角代码编写_杨辉三角怎样用VB写杨辉三角的完整代码 – 手机爱问
- log4j配置详解(非常详细)
- 腾讯云学生机介绍—校园扶持计划
- python植物大战僵尸 豆约翰_python植物大战僵尸十四之采集太阳(太阳不是同时产生)...
- ROS入门之——浅谈launch
- echart改变折线图和折线点的颜色以及折线点的大小
- context.lookup(java:comp/env/XXX)和直接context.lookup(XXX)的区别
- linux不支持modprobe命令,Linux中modprobe命令起什么作用呢?
- 嵌入式入门学习笔记6:[转]嵌入式开发需要学习哪些东西
- 内核启动错误:use vmalloc=size to increase size.
- 超像素分割算法————综述
- 从此我该怎么过 很感人的Flash歌曲
- 平安区块链FiMAX破解吞吐量和延时性困局 性能大幅提升
热门文章
- IM云将给在线教育带来哪些变革?
- 你的创业余额还够么—网易阿里大咖带你领略大数据
- [2018.12.26]BZOJ1022 [SHOI2008]小约翰的游戏John
- 前端基础21:正则基础
- System.InvalidOperationException:“线程间操作无效: 从不是创建控件“txtPortName02”的线程访问它。”...
- samba服务的搭建
- python之SQLAlchemy ORM
- SpringMVC默认欢迎页面的问题
- golang文件操作
- NVMe SSD是什么?