Java不可不知的泛型使用
前面的文章:
本文介绍了Java的泛型的基本使用。
1. 为什么使用泛型
看下面一个例子:
为了说明问题,本类写的尽量简陋,请把目光主要放在类型上。
/**
* @author Xing Xiaoguan (xingrenguanxue)
*/
public class MyArrayList {
private int[] elementData;
private int size = 0;
public MyArrayList(int capacity) {
elementData = new int[capacity];
}
//向数组中添加元素
public void add(int i) {
if (size == elementData.length) {
throw new IndexOutOfBoundsException("数组已满");
}
elementData[size++] = i;
}
//从数组中根据下标获取元素
public int get(int index) {
if (index < 0 || index > size - 1) {
throw new IndexOutOfBoundsException("超出范围");
}
return elementData[index];
}
@Override
public String toString() {
return "MyArrayList{" +
"elementData=" + Arrays.toString(elementData) +
'}';
}
}
该类很简单:有两个成员变量,elementData是一个数组,size是数组中元素的数量。add和get方法能添加和获取元素。
下面测试一下:
public class Test {
public static void main(String[] args) {
MyArrayList myArrayList = new MyArrayList(4);
myArrayList.add(111); //向数组中添加3个int元素
myArrayList.add(222);
myArrayList.add(333);
int i = myArrayList.get(0); //获取
System.out.println(i);
//以上正常运行
myArrayList.add("行小观"); //添加一个String元素,类型不匹配,报错
}
}
向数组中添加3个int类型的元素并能获取,这没问题。
但是如果我们的场景不再需要int类型的元素,而是需要String类型的,那怎么办?
很显然,继续使用该类会报错,报错的原因很简单:我们向数组中添加的元素是String类型的,而数组和方法参数类型是int类型。
此时,就得需要再写一份代码,该份代码较之前的并无大修改,只是把int改为String。如果场景继续变怎么办?那就再写一份新代码!
这样太麻烦了!有没有解决办法?有!
我们知道,Object类是所有类的父类,Object类型的变量能够引用任何类型的对象。所以可以将类型改为Object:
/**
* @author Xing Xiaoguan (xingrenguanxue)
*/
public class MyArrayList {
private Object[] elementData;
private int size = 0;
public MyArrayList(int capacity) {
elementData = new Object[capacity];
}
public void add(Object o) { //向数组中添加元素
if (size == elementData.length) {
throw new IndexOutOfBoundsException("数组已满");
}
elementData[size++] = o;
}
public Object get(int index) { //从数组中获取元素
if (index < 0 || index > size - 1) {
throw new IndexOutOfBoundsException("超出范围");
}
return elementData[index];
}
@Override
public String toString() {
return "MyArrayList{" +
"elementData=" + Arrays.toString(elementData) +
'}';
}
}
再测试一下:
public class Test {
public static void main(String[] args) {
//myArrayList 给int元素使用
MyArrayList myArrayList = new MyArrayList(4);
myArrayList.add(111); //向数组中添加3个int元素
myArrayList.add(222);
myArrayList.add(333);
int i = (int) myArrayList.get(0); //获取
System.out.println(i);
//myArrayList 给String元素使用
MyArrayList myArrayList1 = new MyArrayList(4);
myArrayList1.add("aaa");
myArrayList1.add("bbb");
myArrayList1.add("ccc");
String str = (String) myArrayList1.get(1);
System.out.println(str);
}
}
发现可以向数组中添加和获取int或String类型的元素,这证明该类的数组和方法同时对各种类型的数据都有用,不必再添加额外代码。
但是这样又出现了两个问题:
第一:从数组中获取元素时,需要强制转换类型才行。
int i = (int) myArrayList.get(0);
第二:同一个数组可以添加各种类型的元素。
myArrayList.add(111); //int类型
myArrayList.add("222"); //String类型
myArrayList.add(true); //布尔类型
这就导致了当我们从数组中获取某个元素时,很难知道它的确切类型,往往会强转类型失败。
int i = (int)myArrayList.get(1); //本来是String类型的值,但我提前不知道,拿int变量接收,报错
那这个问题有没有解决办法呢?
有!用泛型!
2. 泛型类
使用泛型改造MyArrayList:
/**
* @author Xing Xiaoguan (xingrenguanxue)
*/
public class MyArrayList <T> {
private T[] elementData;
private int size = 0;
public MyArrayList(int capacity) {
elementData = (T[]) new Object[capacity];
}
public void add(T o) { //向数组中添加元素
if (size == elementData.length) {
throw new IndexOutOfBoundsException("数组已满");
}
elementData[size++] = o;
}
public T get(int index) { //从数组中获取元素
if (index < 0 || index > size - 1) {
throw new IndexOutOfBoundsException("超出范围");
}
return elementData[index];
}
@Override
public String toString() {
return "MyArrayList{" +
"elementData=" + Arrays.toString(elementData) +
'}';
}
}
测试:
public class Test {
public static void main(String[] args) {
//myArrayList 给int元素使用
MyArrayList<Integer> myArrayList = new MyArrayList<>(4);
myArrayList.add(111); //向数组中添加3个int元素
// myArrayList.add("222"); //添加非Integer元素报错
int i = myArrayList.get(1); //无需强制转型
System.out.println(i);
}
}
经过改造,我们把MyArrayList类改为了一个泛型类,它是一个具有多个类型变量的类。
泛型类的声明方式:引入一个类型变量,如T,然后使用<>将其括起来,放在类名后。
public class MyArrayList <T> {
//......
}
如何理解类型变量?它就类似于数学中函数中的变量x,用来代替具体的值:
f(x) = 3x + 1
类型变量的名称可以随便取,一般使用大写字母表示,比如E、K、V、T等。
泛型类中的成员变量、方法参数和返回值的类型都使用类型变量代替:
private T[] elementData;
public void add(T o) {
//.......
}
public T get(int index) {
//......
}
当然,一个泛型类可以有多个类型变量:
public class MyClass <K, V> {
//......
}
当我们需要实例化泛型类时,就使用具体的类型来替换类型变量(T):
MyArrayList<Integer> myArrayList = new MyArrayList<>(4);
该过程就相当于向数学函数中代入数值:
f(3) = 3*3+1 = 10
3. 泛型方法
当我们声明了一个泛型类后,可以很自然地在类内部使用泛型方法。
其实,当类是普通类时,我们仍然能够使用泛型方法。下面是一个例子:
/**
* @author Xing Xiaoguan (xingrenguanxue)
*/
public class PrinterVar {
//该方法接收一个T类型的变量,打印并返回该变量
public <T> T print(T var) {
System.out.println(var);
return var;
}
public static void main(String[] args) {
PrinterVar printerVar = new PrinterVar();
String var = printerVar.print("aa");//String类型
Integer var1 = printerVar.print(12);//int类型
System.out.println(var + " " + var1);
}
}
4. 关于我
点击这里认识我 。 (^o^)/
Java不可不知的泛型使用的更多相关文章
- 《徐徐道来话Java》(2):泛型和数组,以及Java是如何实现泛型的
数组和泛型容器有什么区别 要区分数组和泛型容器的功能,这里先要理解三个概念:协变性(covariance).逆变性(contravariance)和无关性(invariant). 若类A是类B的子类, ...
- JAVA提高六:泛型
在面向对象编程语言中,多态算是一种泛化机制.例如,你可以将方法的参数类型设置为基类,那么该方法就可以接受从这个基类中导出的任何类作为参数,这样的方法将会更具有通用性.此外,如果将方法参数声明为接口,将 ...
- 【原】Java学习笔记027 - 泛型
package cn.temptation.test; import java.util.ArrayList; import java.util.Iterator; public class Samp ...
- Java Collections API和泛型
Java Collections API和泛型 数据结构和算法 学会一门编程语言,你可以写出一些可以工作的代码用计算机来解决一些问题,然而想要优雅而高效的解决问题,就要学习数据结构和算法了.当然对数据 ...
- Java中HashMap(泛型嵌套)的遍历
//Studnet package yzhou.gen03; public class Student<T> { private T score; public T getScore() ...
- Java基础教程:泛型基础
Java基础教程:泛型基础 引入泛型 传统编写的限制: 在Java中一般的类和方法,只能使用具体的类型,要么是基本数据类型,要么是自定义类型.如果要编写可以应用于多种类型的代码,这种刻板的限制就会束缚 ...
- java为什么要用类型擦除实现泛型?--c++,java,c# 的泛型是如何实现的
所以总结一下c++,java,c#的泛型.c++的泛型在编译时完全展开,类型精度高,共享代码差.java的泛型使用类型擦出,仅在编译时做类型检查,在运行时擦出,共享代码好,但是类型精度不行.c#的泛型 ...
- 小白学Java:老师!泛型我懂了!
目录 小白学Java:老师!泛型我懂了! 泛型概述 定义泛型 泛型类的定义 泛型方法的定义 类型变量的限定 原生类型与向后兼容 通配泛型 非受限通配 受限通配 下限通配 泛型的擦除和限制 类型擦除 类 ...
- 剖根问底:Java 不能实现真正泛型的原因是什么?
大家好,我是二哥呀! 今天我来给大家讲一下,Java 不能实现真正泛型的原因是什么? 本文已同步至 GitHub <教妹学 Java>专栏,风趣幽默,通俗易懂,对 Java 初学者亲切友善 ...
随机推荐
- bzoj3446[Usaco2014 Feb]Cow Decathlon*
bzoj3446[Usaco2014 Feb]Cow Decathlon 题意: FJ有n头奶牛.FJ提供n种不同的技能供奶牛们学习,每头奶牛只能学习一门技能,每门技能都要有奶牛学习. 第i头奶牛学习 ...
- 关于在JSP页面识别不了EL表达式的情况
今天在JSP页面接收Controller返回的数据user_nickname,使用EL表达式显示数据发现在页面输出的始终是字符串${user_nickname} 经过查阅资料,问题在于使用的web.x ...
- Ethical Hacking - GAINING ACCESS(8)
Server Side Attacks NeXpose - configure and launch a scan Configure and initialize the application. ...
- 【计网】图解HTTP常见知识点总结
目录 目录 目录 初识TCP/IP TCP/IP协议族4层模型 初识HTTP 请求和响应 HTTP报文 HTTP状态码 HTTP报文首部 其他的首部字段 确保WEB安全的HTTPS HTTPS工作原理 ...
- echarts 实战 : 标题的富文本样式
官方文档在这一块交待的不是很清楚,记录一下. title:{ left:15, top:10, subtext:"AAA {yellow|316} BBB {blue|219}", ...
- 7.CSMA协议
载波监听多路访问协议CSMA CS:载波侦听/监听,每一个站在发送数据之前要检测一下总线上是否有其他计算机在发送数据. MA:多点接入,表示许多计算机以多点接入的方式连接在一根总线上 协议思想:发送帧 ...
- 通过PHP工具箱-站点域名管理(创建本地虚拟主机)
工具:php程序员工具箱(网上很多请自己搜索下载) 1.点击其它选项菜单 -> 选择站点域名管理.如下图 2.进入站点域名管理.如下图(初始的时候,站点为空) 3.设置站点管理.如下图 网站域名 ...
- [jvm] -- 类加载器及双亲委派模板篇
类加载器 JVM 中内置了三个重要的 ClassLoader BootstrapClassLoader(启动类加载器):最顶层的加载类,由C++实现,负责加载 %JAVA_HOME%/lib目录下的j ...
- 时间复杂度为O(nlogn)的排序算法
时间复杂度为O(nlogn)的排序算法(归并排序.快速排序),比时间复杂度O(n²)的排序算法更适合大规模数据排序. 归并排序 归并排序的核心思想 采用"分治思想",将要排序的数组 ...
- DDD之5限界上下文-定义领域边界的利器
上图是一张普通地图,最刺眼的就是边界? 非常好奇地图绘制工程师是如何描绘如此弯曲多变的边界的?强制行政区域还是人群历史原因自然的人以群分? 我们再换个视角,对工程师或者架构师来说,微服务的边界如何划分 ...