泛型
1.泛型
1.1 泛型概述
润物细无声!!!
泛型其实也是一种归纳总结思想的提升,对于数据处理的范围,参数的类型,方法操作的数据… 进行了二次剥离!!!
代码中使用泛型之后,可以极大的提高对于代码的复用性,可移植性,可操作性。
1.2 泛型格式
Java中泛型规定:
<自定义无意义英文大写单字母占位符>
一般常用:
Type
Element
Key
Value
泛型可以约束
方法
类
接口
1.3 【 补充知识点包装类】
Java中数据类型其实是分两大类
1. 基本数据类型
byte short int long float double char boolean
2. 引用数据类型
Java中有一个概念
万物皆对象。
所以Java中对于基本数据类型也做了封装,完成了对于基本数据类型的【包装类】
基本数据类型对应的包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
1. 包装类的使用,可以满足Java中万物皆对象的操作规范,让代码操作更加统一化
2. 包装类中对于数据类型提供了一定的【解析方法】,这些方法是需要我们学习的
3. 包装类使用不会增加我们开发的压力,因为Java中提供了【自动包装】和【自动拆箱】
自动包装和自动拆箱
package com.qfedu.a;
/*
* 自动包装和自动拆箱演示
*/
public class Demo1 {
public static void main(String[] args) {
// 基本数据类型
int num1 = 10;
// 基本数据类型赋值给包装类型,【自动包装】
Integer num2 = num1;
System.out.println(num2);
// 包装类型可以直接赋值对应的基本数据类型,【自动拆箱】
int num3 = num2;
System.out.println(num3);
}
}
1.4 泛型在方法中使用【重点】
1.4.1 格式概述
格式:
权限修饰符 [static] <自定义泛型> 返回值类型 方法名(形式参数列表) {
}
【重点】
1. 要求形式参数列表中必须有一个参数是当前自定义泛型,因为需要通过参数来约
束当前方法运行过程中泛型对应的具体数据类型是哪一个。
2. 返回值类型可以使用自定义泛型,而且是被形式参数列表中传入的泛型对应具体
数类型控制
3. 方法体内也可以使用自定义泛型,同时也是被参数当中泛型对应具体数据类型约
束监控
1.4.2 代码演示
package com.qfedu.a;
/*
-
自定义泛型在方法中使用【重点】
*/
public class Demo2 {
public static void main(String[] args) {
// Integer com.qfedu.a.Demo2.getType(Integer t)
Integer type = getType(1);Float type2 = getType(3.14F); Demo2 type3 = getType(new Demo2()); String type4 = getType("这方法有点东西哦~~~~");
}
/**
- 带有自定义泛型 T 对应的方法
- @param 自定义泛型无意义英文单个大写字母占位符
- @param t T类型
- @return T类型
*/
public static T getType(T t) {
return t;
}
}
1.4.3 自定义泛型使用案例
package com.qfedu.a;
/*
* 自定义泛型在方法中使用案例
*/
public class Demo3 {
public static void main(String[] args) {
printEverything(1);
printEverything(3.14);
printEverything(3.14F);
printEverything("想啥来啥~~~~");
printAnyTypeArray(new Integer[10]);
printAnyTypeArray(new String[10]);
printAnyTypeArray(new Demo3[10]);
}
/*
* 需求
* 完成一个可以满足任意类型展示的方法
*/
public static <T> void printEverything(T t) {
System.out.println(t);
}
/*
* 需求
* 完成一个方法可以满足展示任意类型数组
*/
public static <T> void printAnyTypeArray(T[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
/*
public static void printIntArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
public static void printDoubleArray(double[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
*/
/*
* 一下方法操作过程是一致的,处理方式是一致的,只有参数类型不一样
* 封装 升华参数类型,使用泛型替代
public static void printInt(int i) {
System.out.println(i);
}
public static void printDouble(double d) {
System.out.println(d);
}
public static void printFloat(float f) {
System.out.println(f);
}
*/
}
1.4.4 Object和泛型的对比
package com.qfedu.a;
/*
* Object和泛型的对比
*
*/
public class Demo4 {
public static void main(String[] args) {
/*
* Object参数操作
* 代码中所有的返回值都是Object类型,然后需要操作对应的真实类型
* 要求完成强制类型转换,操作对于数据没有影响,但是有点繁琐
*/
Object type = getType(1);
Object type2 = getType("你好");
Object type3 = getType(new Demo1());
/*
* 泛型参数操作
* 泛型约束之后,返回值类型就是对应的具体数据类型,不需要强制类型
* 转换操作,提高了开发效率
*/
Integer type22 = getType2(1);
String type23 = getType2("你好");
Demo1 type24 = getType2(new Demo1());
}
public static Object getType(Object obj) {
return obj;
}
public static <T> T getType2(T t) {
return t;
}
}
1.5 泛型在类中使用【重点】
1.5.1 格式概述
有那么一大堆方法都要使用泛型,而且这些方法还有一定的关联性。使用类【封装】这些方法,将泛型的声明用于类名上,做到一个统一的约束过程。
【始终强调的是方法!!!】
格式:
class 类名<自定义泛型无意义英文大写单个字母占位符> {
// 成员变量不推荐使用泛型,存在一定的隐患,操作不方便
// 成员方法可以使用类名声明的自定义泛型
// 静态成员方法是个坑!!!
}
类名使用泛型约束具体数据类型的格式【重点】
案例:
class Demo {
}
Eclipse:
类名<具体数据类型> 类对象 = new 构造方法<具体数据类型>();
例:
Demo<String> demo = new Demo<String>(); √标准格式
Demo<String> demo = new Demo<Integer>(); 类型不一致报错 ×
Demo<Object> demo = new Demo<String>(); 类型不一致报错 ×
IDEA:
类名<具体数据类型> 类对象 = new 构造方法<>();
例如:
Demo<String> demo = new Demo<>(); √标准格式
1.5.2 代码演示
package com.qfedu.a;
/*
-
类声明带有自定义泛型演示
-
声明自定义泛型T
/
class Demo {
/- 在带有泛型的类内,成员方法可以直接使用类声明的自定义泛型
*/
public void test1(T t) {
System.out.println(t);
}
public T test2(T t) {
return t;
}
} - 在带有泛型的类内,成员方法可以直接使用类声明的自定义泛型
public class Demo5 {
public static void main(String[] args) {
/*
* 约束泛型的具体数据类型是String类型
*
* 类内所有的成员方法中使用到泛型的位置都是String类型
*/
Demo demo = new Demo();
demo.test1("你好");
String test2 = demo.test2("大家好");
/*
* 泛型约束当前 d 对象中所有的泛型都是Demo1类型
*/
Demo<Demo1> d = new Demo<Demo1>();
d.test1(new Demo1());
Demo1 test22 = d.test2(new Demo1());
/*
* 未使用具体数据类型来约束对应的泛型
*
* 当前d1调用所有类内使用泛型的方法,泛型对应的具体数据类型
* 都会变成Object类型,这样就有有悖于泛型的操作要求。
*/
Demo d1 = new Demo();
}
}
1.5.3 泛型在类内使用的小总结
- 类内的成员方法可以直接使用对应的类名声明泛型
- 类内成员方法使用的泛型具体数据类型是在创建当前类对象是约束。
- 在创建当前类对象时没有约束泛型对应的具体数据类型,那么所有使用到泛型的位置都是Object类型,有悖于泛型使用原则
- 类中使用泛型,是在满足代码普适性的情况下,有可以兼顾数据类型一致化要求。有的放矢
1.5.4 静态成员方法能不能使用类名声明的自定义泛型
类名声明的自定义泛型是什么时候确定对应具体数据类型?
这里是在调用构造方法时,创建当前对象过程中,对于当前泛型约束具体数据类型进行操作。
Demo d = new Demo();
静态成员方法是什么时候加载的?加载有什么要求?
当前程序运行过程中对于当前类有需求,需要加载当前类文件,同时会加载当前类文件中的所有静态资源
静态成员方法在加载的过程需要明确所有运行所需的必要条件。已经是可以直接运行的状态。
类名声明的自定义泛型是在对象创建过程中明确数据类型的,但是静态成员方法是在类文件加载阶段就需要明确所有内容。生存周期不合理!!!
【没有对象】
【注意】
在带有自定义泛型的类内,静态成员方法需要使用泛型,自娱自乐,自己定义泛型,自己使用。参考【泛型在方法中使用】
1.6 泛型在接口中使用【重点】
1.6.1 格式概述
接口:
成员变量:
缺省属性: public static final
定义时必须初始化
成员方法:
缺省属性: public abstract
该方法没有方法体
泛型在接口中使用的格式:
interface 接口名<自定义泛型无意义英文单个字母大写占位符> {
自定义泛型有且只能给方法使用!!!
}
【接口中的成员变量不能使用自定义泛型】
1. 成员变量缺省属性public static final 定义时必须初始化,但是泛型初始
化为什么???
没有具体的数据类型,无法进行初始化操作
2, 成员变量static修饰,当前成员变量需要在加载阶段,明确所有内容,但是当
前情况下,数据类型不明确的。
有悖于语法的!!!
1.6.2 带有泛型的接口怎么用
接口没有自己的类对象
1. 接口不是类
2. 接口中存在未完成的方法,就算是有对象,也得报错的。
带有泛型的接口使用需要依赖于实现类完成。这里有两种方式
1. 自由
2. 乖巧
1.6.3 代码演示
package com.qfedu.a;
/**
* 带有自定义泛型的接口
*
* @author Anonymous
*
* @param <T> 自定义泛型
*/
interface A<T> {
/**
* 当前成员方法缺省属性为public abstract 没有方法体
* @param t T类型
* @return T类型
*/
T test(T t);
}
/**
* 自由方式
* 实现类使用和接口相同的泛型,泛型对应具体数据类型在创建
* 当前类对象时明确
*
* @author Anonymous
*
* @param <T>
*/
class TypeA<T> implements A<T> {
@Override
public T test(T t) {
System.out.println("未明确泛型具体数据类型操作方式,自由");
return t;
}
}
/**
* 乖巧模式
* 实现类在遵从带有自定义泛型的接口时,接口中泛型的具体数据类型已经确认
* 实现类完成的对应方法就OK
*
* @author Anonymous
*
*/
class TypeB implements A<String> {
@Override
public String test(String t) {
// TODO Auto-generated method stub
return "你好";
}
}
public class Demo7 {
public static void main(String[] args) {
// 在创建对象的过程过程中约束泛型的具体数据类型。
TypeA<Integer> t1 = new TypeA<Integer>();
t1.test(1);
TypeA<Demo1> t2 = new TypeA<Demo1>();
t2.test(new Demo1());
// typeB完成的方法test,要求的参数已经被接口约束
TypeB typeB = new TypeB();
typeB.test("123123");
}
}