目录
一、包装类
1.1 基本数据类型和对应的包装类
1.2 装箱和拆箱
二、泛型
2.1 语法
三、泛型类的使用
3.1 语法
3.2 示例
3.3 类型推导
3.4 裸类型
四、泛型如何编译的
4.1 擦除机制
4.2 为何不能实例化泛型类型数组
五、泛型的上界
5.1 语法
5.2 示例
5.3 复杂示例
六、泛型方法
6.1 定义语法
6.2 示例
定义:在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型。
//装箱:将一个基本数据类型转换为引用数据类型
int a=10;
Integer value=a;//自动装箱
Integer value1=Integer.valueOf(20);//显示装箱
Integer value2=new Integer(20);//显示装箱
System.out.println(value+" "+value1+" "+value2);
//拆箱:将Integer对象中的值取出,放到一个基本数据类型中
int num=value1;//自动拆箱
int num1=value1.intValue();
在上述使用过程中,装箱和拆箱带来不少的代码量,所以为了减少开发者负担,Java提供了自动机制。
面试题
public static void main(String[] args) {
Integer a=127;
Integer b=127;
System.out.println(a==b);
Integer c=128;
Integer d=128;
System.out.println(c==d);
Integer e=Integer.valueOf(2);
}
//结果:
true
false
上述结果输出不同是因为在装箱过程中返回的引用类型不一样:
泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。
class 泛型类名称<类型形参列表>{
//这里可以使用类型参数
}
class ClassName{
}
class 泛型类名称<类型形参列表>extends 继承类/*这里可以使用类型参数*/{
//这里可以使用类型参数
}
class ClassNameextends ParentClass{
//这里可以使用部分类型参数
}
代码实现:
class MyArray{
//T[]kk=new T[10];//语法错误,泛型当中不能实例化泛型类型的数组
public T[]array=(T[])new Object[10];
public T getPos(int pos){
return this.array[pos];
}
public void setArray(int pos,T val){
this.array[pos]=val;
}
}
public class Test {
public static void main(String[] args) {
MyArraymyArray=new MyArray<>();//1 指定当前类型
myArray.setArray(0,1);
//myArray.setArray(1,"akai");//此时因为在注释1处指定类当前类型,此时编译器会在存放元素时帮助我们进行类型检查
System.out.println(myArray.getPos(0));
}
}
代码解释:
1.类名后的
了解:【规范】类型形参一般使用一个大写字母,常用的名称有:
·E表示Element
·K表示Key
·V表示Value
·N表示Number
·T表示Type
·S,U,V 等等-第二、第三、第四个类型
泛型类<类型实参>变量名;//定义一个泛型类引用
new 泛型类<类型实参>(构造方法实参);//实例化一个泛型类对象
MyArray
list=new MyArray ();
注意: 泛型只接受类,所有的基本数据类型必须使用包装类
当编译器可以根据上下文推导出类型实参时,可以省略类型室参的填写
MyArray
list=new MyArray<>();//可以推导出实例化需要的类型实参为Integer
裸类型是一个泛型类但没有类型实参,例如:
MyArray list=new MyArray();
注:裸类型是为了兼容老版本的API保留的机制下面的类型擦除部分
小结:
1.泛型是将数据类型参数化,进行传递
2.使用
3.泛型类目前位置的优点:数据类型参数化,编译时自动进行类型检查和转换
在编译过程当中,将所有的T替换为Obeject这种机制,我们成为:擦除机制。
Java的泛型机制是在编译级别实现的。编译器生成的字节码文件但在运行期间并不包含泛型的类型信息。
class MyArray{
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 class Test {
public static void main(String[] args) {
MyArraymyArray1=new MyArray<>();
Integer[]Strings =myArray1.getArray();
}
}
/*Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
at Test.main(Test.java:27)*/
原因:替换后的方法为(由于运行时没有泛型机制,因此擦除机制在编译时期将T转换为Object):将Object[]分配给Intefer[]引用,程序报错。
public Object[]getArray{
return array;
}
通俗讲就是:返回的Object数组里面,可能存放的是任何的数据类型,可能是String,可能是Person,运行的时候,直接转给Integer类型的数组,编译器认为是不安全的。
正确的使用方法:【了解即可】
class MyArray {
public T[] array;
public MyArray() {
}
//涉及到映射
public MyArray(Class 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 class Test {
public static void main(String[] args) {
MyArray myArray1 = new MyArray<>(Integer.class,10);
Integer[] integers = myArray1.getArray();
}
}
泛型的常用方法:
public Objiect[] obj=new Object [3];
public E getPos(int pos){
return (E)obj[pos];
}
class 泛型类名称<类型形参 extends 类型边界>{
...
}
public class MyArray{
...
}
此时只接受Number的子类作为E的类型实参
MyArray
a1;//正常,因为Integer 是Number的子类型 MyArray
a2;//编译错误,因为String 不是Number的子类型
注: 没有指定类型边界的E,会默认擦除为Object类型。
//class Alg 1
//E必须是实现了Com[arable方法的
class Alg>{
public E findMax(E []array){
E max=array[0];
for (int i = 1; i < array.length; i++) {
//if(max alg=new Alg<>();
Integer []array={1,3,5,6,2};
Integer val=alg.findMax(array);
System.out.println(val);
}
}
方法限定符<类型形参列表>返回类型 方法名称 (形参列表){
...
}
public class Test {
//返回类型可以为任意类型,E也可以
public static void swap(E[]array,int i,int j){
E t=array[i];
array[i]=array[j];
array[j]=t;
}
}
代码示例:
public class Test{
//返回类型可以为任意类型,E也可以
public static void swap(E[]array,int i,int j){
E t=array[i];
array[i]=array[j];
array[j]=t;
}
public static void main(String[] args) {
Integer []a={1,3,5,6,2};
Integer []b={2,3,2,1,5,6};
//可以a的类型推导E,因此<>中可以省略
Test.swap(a,2,3);
Test.swap(b,2,1);
System.out.println(a[3]);
System.out.println(b[1]);
}
}
?用于在泛型使用,即为通配符
class Message{
private T message;
public T getMessage(){
return message;
}
public void setMessage(T message){
this.message=message;
}
}
public class Common {
public static void main(String[] args) {
Message message=new Message<>();
message.setMessage("zhonghuizhaodaoni");
func(message);
}
//如果此时泛型的类型设置的不是String,而是Integer,就会出现错误,而此时使用通配符 ? 就会很好的解决这个问题
public static void func(Message temp){
System.out.println(temp.getMessage());
}
}
在"?"的基础上产生了两个字通配符:
extends 上界>
extends Number> //可以传入的实参类型是Number或者Number的子类
语法:
extends 上界>
extends Number> //可以传入的实参类型是Number或者Number的子类
class Food {
}
class Fruit extends Food {
}
class Apple extends Fruit {
}
class Banana extends Fruit {
}
class Plate{
private T message;
public T getMessage(){
return message;
}
public void setMessage(T message){
this.message=message;
}
}
public class Common {
public static void main(String[] args) {
Plate plate=new Plate<>();
plate.setMessage(new Apple());
func(plate);
Plate plate1=new Plate<>();
plate1.setMessage(new Banana());
func(plate1);
}
//此时使用通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,多以无法修改
public static void func(Plate extends Fruit> temp){
//temp.setMessage(new Banana());//仍然无法修改
//temp.setMessage(new Apple());//仍然无法修改
System.out.println(temp.getMessage());
}
}
此时无法确定fun函数中对temp进行设置元素,因为temp接收的是Fruit和它的子类,此时存储的元素应该是哪个子类无法确定。所以添加会报错!但是可以获取元素。
public static void func(Plate extends Fruit> temp){
//temp.setMessage(new Banana());//仍然无法修改
//temp.setMessage(new Apple());//仍然无法修改
//System.out.println(temp.getMessage());
Fruit b=temp.getMessage();
System.out.println(b);
}
通配符的上界,不能进行写入数据,只能进行读取数据。
语法:
super 下界>
super Integer> //代表可以传入的实参的类型是Integer或者Integer的父类类型
class Food {
}
class Fruit extends Food {
}
class Apple extends Fruit {
}
class Banana extends Fruit {
}
class Plate{
private T message;
public T getMessage(){
return message;
}
public void setMessage(T message){
this.message=message;
}
}
public class Common {
public static void main(String[] args) {
Plate plate=new Plate<>();
plate.setMessage(new Fruit());
func(plate);
Plate plate1=new Plate<>();
plate1.setMessage(new Apple());
//func(plate1);//添加的必须是Fruit或者其父类
}
//此时使用通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,多以无法修改
public static void func(Plate super Fruit> temp){
//此时可以修改!添加的是Fruit或者Fruit的子类,所以可以向上转型
temp.setMessage(new Banana());
temp.setMessage(new Fruit());
//temp.setMessage(new Food());//添加失败,因为若是Fruit的父类,则需要向下转型,向下转型是不安全的
//Fruit fruit= temp.getMessage();//不能接收,这里无法确定是哪个父类,需要向下转型为Fruit,此时也是不安全的
System.out.println(temp.getMessage());//只能直接输出
}
}
通配符的下界,不能进行读取数据,只能写入数据