目录
一,包装类
1.基本数据类型和对应的包装类
2.装箱和拆箱
3.自动装箱和自动拆箱
二,什么是泛型?
三,引出泛型
语法
四,泛型类的使用
1.语法
2.类型推导(Type Inference)
五,裸类型(Raw Type)
六,泛型是如何编译的
1.擦除机制
2.为什么不能实例化泛型类型数组
七,泛型的上界
1.语法
2.示例
3.复杂示例
八,泛型方法
1.定义语法
2.示例
3.使用实例-可以类型推导
4.使用实例-不可以类型推导
一,包装类
1.基本数据类型和对应的包装类
基本数据类型 | 包装类 | 内存大小(单位:字节) |
byte | Byte | 1 |
short | Short | 2 |
int | Integer | 4 |
long | Long | 8 |
float | Float | 4 |
double | Double | 8 |
char | Character | 2 |
boolean | Boolean | 未定义 |
2.装箱和拆箱
int i = 10;
// 装箱操作,新建一个 Integer 类型对象,将 i 的值放入对象的某个属性中
Integer i = 10;隐式装箱
Integer ii = Integer.valueOf(i);//显示装箱
Integer ij = new Integer(i);
// 拆箱操作,将 Integer 对象中的值取出,放到一个基本数据类型中
int j = ii.intValue();
3.自动装箱和自动拆箱
int i = 10;
Integer ii = i; // 自动装箱
Integer ij = (Integer)i; // 自动装箱
int j = ii; // 自动拆箱
int k = (int)ii; // 自动拆箱
public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b);
System.out.println(c == d);
}
答案:true false
解析:
这里我们需要先了解Integer的源码
我们发现定义的i的值满足一定范围,点进而这个low和high的范围正好满足[-128,127],所以这个数组则有下标[0,255]这256个下标,而返回的这个数组的下标则满足i+(-(-128)),也就是i+128下标对应的元素,当i+128>255时则会产生新的对象,所以上述代码中c和d的地址已不相同,所以输出的值才会是false。
二,什么是泛型?
三,引出泛型
class MyArray {
public Object[] array = new Object[10];
public Object getPos(int pos) {
return this.array[pos];
}
public void setVal(int pos,Object val) {
this.array[pos] = val;
}
}
public class TestDemo {
public static void main(String[] args) {
MyArray myArray = new MyArray();
myArray.setVal(0,10);
myArray.setVal(1,"hello");//字符串也可以存放
String ret = myArray.getPos(1);//编译报错
System.out.println(ret);
}
}
语法
class 泛型类名称<类型形参列表> {
// 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> {
}
class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ {
// 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> {
// 可以只使用部分类型参数
}
上述代码进行改写如下:
class MyArray<T> {
Object[] array = new Object[10];
public void setValue(int pos,T val) {
array[pos] = val;
}
public T getValue(int pos) {
return (T)array[pos];
}
}
public class Main {
public static void main(String[] args) {
MyArray<Integer> myArray= new MyArray<Integer>();
myArray.setValue(1,1);
int i = myArray.getValue(1);
MyArray<String> myArray1 = new MyArray<String>();
myArray1.setValue(0,"hehe");
String str = myArray1.getValue(0);
}
}
• E 表示 Element• K 表示 Key• V 表示 Value• N 表示 Number• T 表示 Type• S, U, V 等等 - 第二、第三、第四个类型
T [] ts = new T [ 5 ]; // 是不对的
四,泛型类的使用
1.语法
泛型类 < 类型实参 > 变量名 ; // 定义一个泛型类引用new 泛型类 < 类型实参 > ( 构造方法实参 ); // 实例化一个泛型类对象
MyArray < Integer > list = new MyArray < Integer > ();
2.类型推导(Type Inference)
MyArray < Integer > list = new MyArray <> (); // 可以推导出实例化需要的类型实参为 Integer
五,裸类型(Raw Type)
说明:
MyArray list = new MyArray ();
六,泛型是如何编译的
1.擦除机制
2.为什么不能实例化泛型类型数组
代码1:
class MyArray<T> {
public T[] array = (T[])new Object[10];
public T getPos(int pos) {
return this.array[pos];
}
public void setVal(int pos,T val) {
this.array[pos] = val;
}
public T[] getArray() {
return array;
}
}
public static void main(String[] args) {
MyArray<Integer> myArray1 = new MyArray<>();
Integer[] strings = myArray1.getArray();
}
/*
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
at TestDemo.main(TestDemo.java:31)
*/
public Object[] getArray() {
return array;
}
class MyArray<T> {
public T[] array;
public MyArray() {
}
/**
* 通过反射创建,指定类型的数组
* @param clazz
* @param capacity
*/
public MyArray(Class<T> clazz, int capacity) {
array = (T[])Array.newInstance(clazz, capacity);
}
public T getPos(int pos) {
return this.array[pos];
}
public void setVal(int pos,T val) {
this.array[pos] = val;
}
public T[] getArray() {
return array;
}
}
public static void main(String[] args) {
MyArray<Integer> myArray1 = new MyArray<>(Integer.class,10);
Integer[] integers = myArray1.getArray();
}
七,泛型的上界
1.语法
class 泛型类名称<类型形参 extends 类型边界> {
...
}
2.示例
public class MyArray<E extends Number> {
...
}
只接受 Number 的子类型作为 E 的类型实参
MyArray<Integer> l1; // 正常,因为 Integer 是 Number 的子类型
MyArray<String> l2; // 编译错误,因为 String 不是 Number 的子类型
error: type argument String is not within bounds of type-variable EMyArrayList<String> l2;^where E is a type-variable:E extends Number declared in class MyArrayList
3.复杂示例
写一个泛型类,求一个数组中的最大值
面对这样一个问题,我们可能首先会写出以下代码:
此时if处为什么会报错呢?
原因:T一定是一个引用类型,最终被擦除为Object类型,而T类型则一定要是可比较的,不然无法进行比较。
所以:怎么约束这个T一定是可以比较大小的?
很简单,只要让它extends一个Comparable
class Alg<T extends Comparable<T>> /*表示T一定是实现了Comparable接口的*/{
public T FindMax(T[] array) {
T Max = array[0];
for (int i = 1; i < array.length; i++) {
if (Max.compareTo(array[i]) < 0) /*Max如果比array[i]小,则返回的小于0*/{
Max = array[i];
}
}
return Max;
}
}
八,泛型方法
1.定义语法
方法限定符 < 类型形参列表 > 返回值类型 方法名称 ( 形参列表 ) { ... }
简单来说:就是在一个普通的类里面实现泛型方法
2.示例
public class Util {
//静态的泛型方法 需要在static后用<>声明泛型类型参数
public static <E> void swap(E[] array, int i, int j) {
E t = array[i];
array[i] = array[j];
array[j] = t;
}
}
将上述代码以泛型方法的形式来写:
class Alg {
public<T extends Comparable<T>> T FindMax(T[] array) {
T Max = array[0];
for (int i = 1; i < array.length; i++) {
if (Max.compareTo(array[i]) < 0) /*Max如果比array[i]小,则返回的小于0*/{
Max = array[i];
}
}
return Max;
}
}
3.使用实例-可以类型推导
Integer[] a = { ... };
swap(a, 0, 9);
String[] b = { ... };
swap(b, 0, 9);
同样以上述代码为例:
当我们要使用上述代码来获得一个数组中的最大值时
class Alg {
public<T extends Comparable<T>> T FindMax(T[] array) {
T Max = array[0];
for (int i = 1; i < array.length; i++) {
if (Max.compareTo(array[i]) < 0) /*Max如果比array[i]小,则返回的小于0*/{
Max = array[i];
}
}
return Max;
}
}
public class Main {
public static void main(String[] args) {
Alg alg = new Alg();
Integer[] integers = new Integer[]{1,2,3,4,5};
//int ret = alg.<Integer>FindMax(integers);//指定类型
int ret = alg.FindMax(integers);//不指定类型
//类型推导:根据实参传值来推导此时的类型
//当可以类型推导时上两种写法均可
System.out.println(ret);
}
}
4.使用实例-不可以类型推导
Integer[] a = { ... };
Util.<Integer>swap(a, 0, 9);
String[] b = { ... };
Util.<String>swap(b, 0, 9);
完.