泛型(工作能用到的)

本文详细介绍了Java中的泛型概念,包括泛型类、泛型方法、通配符、装箱拆箱等,并通过实例展示了泛型在数组操作、类型安全和比较器等方面的应用。同时,解释了泛型的擦除机制以及边界限制,帮助读者深入理解泛型的使用和设计原则。
摘要由CSDN通过智能技术生成

什么是泛型

Java编程思想》对 泛型的介绍:一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。

因此,泛型:就是适用于多种类型。从代码上讲,就是对类型实现了参数化。

语法

class 泛型类名称 < 类型形参列表 > {
// 这里可以使用类型参数
}
class ClassName < T1 , T2 , ..., Tn > {
}

例如:我们要实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个下标的值

class MyArray2<T>{
    public T[] array=(T[])new Object[10];
    public T getValue(int pos){
        return  array[pos];
    }
    public void setValue(int pos, T val){
        array[pos]=val;
    }


}
public class Test2 {
    public static void main(String[] args) {
        //<>里面指定了类型,说明此时这个类里面只能放这个数据类型的数据
        //并且<>中是引用类型
        MyArray2<String> myArray2=new MyArray2<String>();
        myArray2.setValue(0,"hello");
        myArray2.setValue(1,"world");
        System.out.println(myArray2.getValue(0));
        System.out.println(myArray2.getValue(1));
        MyArray2<Integer> myArray3=new MyArray2<Integer>();
        myArray3.setValue(2,100);
        System.out.println(myArray3.getValue(2));
    }
}

1.<>当中指定类型后,编译器会根据你指定的类型参数来进行类型的检查

2.取元素时不用进行强转

T[]array=new T[10]//error是错误的 

 泛型的使用

语法

泛型类 < 类型实参 > 变量名 ; // 定义一个泛型类引用
new 泛型类 < 类型实参 > ( 构造方法实参 ); // 实例化一个泛型类对象

 例如上面代码:MyArray2<String> myArray2=new MyArray2<Stirng>();//后面<>中的类型可写可不写,编译器可以根据上下文推导出类型实参。

泛型如何编译

擦除机制

 我们通过字节码发现在编译时,所有的T都变成了Object.

 所以擦除机制就是在 编译的时候 把泛型T 擦除成了Object.运行期间没有这个泛型这个概念。

思考:既然T都被擦除成了Object了,为什么不能实例化泛型类型的数组?

T[]array=new T[10]//等价于Object[]array=new Object[10];  错误

class MyArray2<T>{
    public T[] array=(T[])new Object[10];
    public T getValue(int pos){
        return  array[pos];
    }
    public void setValue(int pos, T val){
        array[pos]=val;
    }
    public T[] getArray(){
        return array;
    }
}
public class Test2 {
    public static void main(String[] args) {
       MyArray2 myArray2=new MyArray2();
        myArray2.setValue(0,10);
        myArray2.setValue(1,"hello");
        Integer[] integers= (Integer[]) myArray2.getArray();
        System.out.println(Arrays.toString(integers));
    }
}

  因为返回的Object数组里面,可能存放的是任何的数据类型,可能是String,可能是Person,运行的时候,直接转给Intefer类型的数组,编译器认为是不安全的。

正确的创建方法(了解即可)

class MyArray3<T>{
    T[] array;
    /**
     * 通过放射创建,指定类型的数组
     * @param clazz
     * @param capacity
     */
    public MyArray3(Class<T>clazz,int capacity){
        array=(T[]) Array.newInstance(clazz,capacity);
    }
    public T getPos(int pos){
        return array[pos];
    }
    public void setArray(int pos,T val){
        array[pos]=val;
    }
    public T[] getArray(){
        return array;
    }
}
public class Test3 {
    public static void main(String[] args) {
        MyArray3<Integer> myArray3=new MyArray3<>(Integer.class,10);
        myArray3.setArray(0,10);
        myArray3.setArray(1,15);
        Integer[] integers=myArray3.getArray();
        System.out.println(Arrays.toString(integers));
    }
}

泛型的边界 

有上边界,无下边界。

语法

class 泛型类名称 < 类型形参 extends 类型边界 > {
...
}

 例如:

public class MyArray < E extends Number > {
...
}

这里的extends不是继承的意思,表示边界是Number及其子类 

应用:实现一个泛型类,找出数组最大值

因为泛型类T不是基本类型,不能简单的进行大于小于这样的比较,T是引用类型要使用比较器,也就是将T擦除成Comparable接口。

class Alg<T extends Comparable<T>>{//将T擦成Comparable接口
    public T findMax(T[] array){
        T max=array[0];
        for (int i = 0; i < array.length; i++) {
            if(max.compareTo(array[i])<0){
                max=array[i];
            }
        }
        return max;
    }
}

泛型方法

1.成员方法:方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { ... }

class Alg{
    public<T extends Comparable<T>> T findMax(T[] array){
        T max=array[0];
        for (int i = 0; i < array.length; i++) {
            if(max.compareTo(array[i])<0){
                max=array[i];
            }
        }
        return max;
    }
}
public class Test {
    public static void main(String[] args) {
        Alg alg=new Alg();
        Integer[] array=new Integer[]{1,23,4,56,7,8};
        System.out.println(alg.findMax(array));
    }
}

56 

2.静态方法,静态的泛型方法 需要在static后用<>声明泛型类型参数

class Alg2{
        //静态方法不依赖于对象的调用
    public static<T extends Comparable<T>> T findMax(T[] array){
        T max=array[0];
        for (int i = 0; i < array.length; i++) {
            if(max.compareTo(array[i])<0){
                max=array[i];
            }
        }
        return max;
    }
}

通配符

? 用于在泛型的使用,即为通配符
通配符是用来解决泛型无法协变的问题的 ,协变指的就是如果 Student Person 的子类,那么 List<Student> 也应该是 List<Person> 的子类。但是泛型是不支持这样的父子类关系的。
泛型 T 是确定的类型,一旦你传了我就定下来了,而通配符则更为灵活或者说是不确定,更多的是用于扩充参数的范围。
举个例子
class Message<T>{
    private T message;

    public T getMessage() {
        return message;
    }

    public void setMessage(T message) {
        this.message = message;
    }
}
public class Test2 {
    public static void func(Message<?> message){
        //message.setMessage(100);//不能修改,因为不知道参数是什么类型的
        System.out.println(message.getMessage());
    }
    public static void main(String[] args) {
        Message<String> message=new Message<>();
        String s="中国世界第一";
        message.setMessage(s);
        func(message);
        Message<Integer> message2=new Message<>();
        Integer a=10;
        message2.setMessage(a);
        func(message2);
    }
}

通过使用通配符,既可以传String也可以传Integer,扩大了参数的范围

通配符的上界(常用于获取元素)

语法

<? extends 上界 >
<? extends Number > // 可以传入的实参类型是 Number 或者 Number 的子类
应用:
class Food{

}
class Fruit extends Food{

}
class Apple extends Fruit{

}
class Banana extends Fruit{

}
class Alg2<T>{
    private T val;

    public T getVal() {
        return val;
    }

    public void setVal(T val) {
        this.val = val;
    }
}
public class Test3 {
    /**
     * ?通配符extends上限是Fruit及其子类
     * @param temp
     */
    public static void func(Alg2<? extends Fruit> temp){
        /*
        Banana banana=temp.getVal();
        Apple apple=temp.getVal();
        不能进行修改,因为temp接收的是Fruit及其子类,无法确定参数是什么类型
         */
        Fruit fruit= temp.getVal();//temp放的是Fruit及其子类,所以可以用Fruit进行接收
        System.out.println(temp.getVal());
    }
    public static void main(String[] args) {
        Alg2<Banana> alg2=new Alg2<>();
        alg2.setVal(new Banana());
        func(alg2);
        Alg2<Apple> alg3=new Alg2<>();
        alg3.setVal(new Apple());
        func(alg3);
    }
}

通配符的下限(常用于设置元素)

语法

<? super 下界 >
<? super Integer > // 代表 可以传入的实参的类型是 Integer 或者 Integer 的父类类型

应用:

class Food{

}
class Fruit extends Food {

}
class Apple extends Fruit {

}
class Banana extends Fruit {

}
class Alg4<T>{
    private T val;

    public T getVal() {
        return val;
    }

    public void setVal(T val) {
        this.val = val;
    }
}
public class Test4 {
    /**
     * 通配符?下限super是Fruit及其父类
     * @param temp
     */
    public static void func(Alg4<? super Fruit> temp){
        /**
         * 因为最小的下限是Fruit,所以可以使用setVal进行修改
         */
        temp.setVal(new Banana());//向上转型
        temp.setVal(new Apple());//向上转型
        /**
         * temp可能是Fruit或者他的父类,无法用子类(Fruit)接收
         */
        //Fruit fruit=temp.getVal();

        System.out.println(temp.getVal());
    }
    public static void main(String[] args) {
        Alg4<Fruit> alg4=new Alg4<>();
        alg4.setVal(new Fruit());
        Alg4<Food> alg41=new Alg4<>();
        alg41.setVal(new Food());
    }
}

(自动)装箱

public static void main(String[] args) {
        int a=10;
        Integer integer1=a;//自动装箱
        Integer integer2=new Integer(a);//显示装箱
        Integer integer3=Integer.valueOf(a);//显示装箱
    }

通过字节码我们发现,装箱的底层是采用Integer.valueOf

(自动)拆箱

public static void main(String[] args) {
        Integer integer=10;
        int val=integer;//自动拆箱
        System.out.println(val);
        int val2=integer.intValue();//显示拆箱
        double val3=integer.doubleValue();//显示拆箱
    }

面试题:输出什么

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);
    }

答案:

true

false 

why?

 装箱的底层是valueOf,查看他的源码发现在【-127,128】范围内有一个数组,不在这个范围内需要new 对象,所以127在数组内相同,128需要new 对象,地址不同

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

指挥部在下面

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值