java之泛型

泛型有两种使用方法:
1、泛型类
2、泛型方法

1 - 注意:

如果不指定泛型的类型,默认是Object类型



2 - 泛型类

案例:
把超级数组改造一下,使用泛型

注意:数组的类型还是Object类型,因为T(泛型)不能被new(实例化),如果数组使用了T类型,那这个数组就创建不起来。不过因为泛型限制传入的类型,所以取数据的时候可以直接强制类型转换

/**
 * 超级数组
 */
public class SuperArray<T> {

    /**
     * 数据数组
     */
    private Object arr[];

    /**
     * 个数
     */
    private int length;

    /**
     * 末尾添加元素
     * @param date
     */
    public void add(T date) {
        if (arr.length == length){
            expansionOfCapacity();
        }
        arr[length++] = date;
        System.out.println("添加成功");
    }

    /**
     * 末尾删除元素
     * @return
     */
    public T del() {
        if (length == 0) {
            return null;
        }
        return (T)arr[--length];
    }

    /**
     * 扩容
     */
    public void expansionOfCapacity() {
        Object[] temp = new Object[arr.length * 2];
        for (int i = 0; i < arr.length; i++) {
            temp[i] = arr[i];
        }
        arr = temp;
    }

    public SuperArray() {
        arr = new Object[10];
        length = 0;
    }

    @Override
    public String toString() {
        return "SuperArray{" +
                "arr=" + Arrays.toString(arr) +
                ", length=" + length +
                '}';
    }
}
// main方法
    public static void main(String[] args) {
        SuperArray<Integer> sa = new SuperArray<>();
        sa.add(10);
        sa.add(52);
        sa.add(95);
        sa.add(1002);
        System.out.println(sa);
    }

创建时左边写了类型,右边直接使用钻石符(<>)就好了,因为可以根据左边钻石符里面写的类型推断出右边是什么类型。
SuperArray<Integer > sa = new SuperArray<>();




3 - 泛型方法

泛型方法没怎么用

泛型方法是通过实参来确定泛型的类型

public class Temp {
    // main方法
    public static void main(String[] args) {
        System.out.println(temp("红红火火恍恍惚惚"));
    }
    
    // 泛型方法
    public static <T> T temp(T o1) {
        return o1;
    }
}




4 - 泛型类的继承

泛型类之间的继承有两种:
1、明确泛型类型
2、不明确泛型类型

4.1 - 明确泛型类型

public class SuperArray implements Super<Student>{}

4.2 - 不明确泛型类型

public class SuperArray<T> implements Super<T>{}

4.3 - 如果有多个

public class SuperArray<T,K,V> implements Super<K,V>, Comparator<T> {}




5 - 泛型通配符

泛型通配符分为三种:
1、无界
2、上界
3、下界

为什么会有泛型通配符?
当泛型类当成方法参数进行传递时,实参的类型和泛型都必须一致,泛型中的类必须一致,父子关系都不行。
所以就有了泛型通配符

泛型通配符常用于传参的时候

这些是下面要用到的类

/**
 * 动物类
 */
public interface Animals {
}

/**
 * 狗类
 */
public class Dog implements Animals{
}

/**
 * 泰迪类
 */
public class Teddy extends Dog{
}

/**
 * 超级数组
 */
public class SuperArray<T>{

    /**
     * 数据数组
     */
    private Object arr[];

    /**
     * 存放到数组里的个数
     */
    private int length;



    /**
     * 末尾添加元素
     * @param date
     */
    public void add(T date) {
        if (arr.length == length){
            expansionOfCapacity();
        }
        arr[length++] = date;
        System.out.println("添加成功");
    }

    /**
     * 末尾删除元素
     * @return
     */
    public T del() {
        if (length == 0) {
            return null;
        }
        return (T)arr[--length];
    }

    /**
     * 根据下标查询
     * @param i
     * @return
     */
    public T select(int i) {
        if (i < 0 || i >= length) {
            return null;
        }
        return (T)arr[i];
    }

    /**
     * 扩容
     */
    public void expansionOfCapacity() {
        Object[] temp = new Object[arr.length * 2];
        for (int i = 0; i < arr.length; i++) {
            temp[i] = arr[i];
        }
        arr = temp;
    }


    public Object[] getArr() {
        return arr;
    }

    public int getLength() {
        return length;
    }

    public SuperArray() {
        arr = new Object[10];
        length = 0;
    }


    @Override
    public String toString() {
        return "SuperArray{" +
                "arr=" + Arrays.toString(arr) +
                ", length=" + length +
                '}';
    }
}

5.1 - 无界

就是在方法形参类型的砖石符中用?(问号),就代表无界。
public static void pringSuperArray(SuperArray<?> arr) {

无界:实参中泛型中的所有类都可以

public class Temp  {

    public static void main(String[] args) {
        SuperArray<Dog> sa = new SuperArray<>();
        sa.add(new Dog());
        sa.add(new Dog());

        pringSuperArray(sa);
    }

	// 用于打印超级数组(重点)
    public static void pringSuperArray(SuperArray<?> arr) {
        for (int i = 0; i < arr.getLength(); i++) {
            System.out.println(arr.select(i));
        }
    }
}

5.2 - 上界

就是在方法形参类型的砖石符中用(? extends 指定类),就代表上界。
public static void pringSuperArray(SuperArray<? extends Dog> arr) {

上界:只有指定类和指定类的子类,才可以传入

简单理解:<? extends Dog>

?:表示要传入的类型

extends:表示继承

Dog:指定类
连起来读就是:传入的类型是继承至指定类的。(包括指定类)

public class Temp  {

    public static void main(String[] args) {
        SuperArray<Dog> sa = new SuperArray<>();
        sa.add(new Dog());
        sa.add(new Dog());

        pringSuperArray(sa);
    }

	// 用于打印超级数组(重点)
    public static void pringSuperArray(SuperArray<? extends Dog> arr) {
        for (int i = 0; i < arr.getLength(); i++) {
            System.out.println(arr.select(i));
        }
    }
}

5.3 - 下界

就是在方法形参类型的砖石符中用(? extends 指定类),就代表上界。
public static void pringSuperArray(SuperArray<? super Dog> arr) {

上界:只有指定类和指定类的子类,才可以传入

简单理解:<? super Dog>

?:表示要传入的类型

super:表示父类

Dog:指定类
连起来读就是:传入的类型是指定类的父类(包括指定类)。

public class Temp  {

    public static void main(String[] args) {
        SuperArray<Dog> sa = new SuperArray<>();
        sa.add(new Dog());
        sa.add(new Dog());

        pringSuperArray(sa);
    }

	// 用于打印超级数组(重点)
    public static void pringSuperArray(SuperArray<? super Dog> arr) {
        for (int i = 0; i < arr.getLength(); i++) {
            System.out.println(arr.select(i));
        }
    }
}




6 - 类型擦除

JAVA的泛型其实是一种伪泛型,在编译期间,所有泛型信息都会被擦除。使用到泛型类的地方都会变成Object
在生成字节码文件中不包含泛型中的类信息,使用泛型的时候加上类型参数,在编译器编译的时候会被去掉,这个过程就叫类型擦除

疑问?
既然编译的时候已经把泛型T擦除为Object,但为什么使用get等其他方法获取数据的时候却收到的是具体的泛型类型,而且不是Object类型?

解答:
java编译的时候进行类型擦除,将所有泛型类都擦除为Object类型,但是在使用泛型类或方法时,编译器会根据上下文信息推断出泛型类型,从而在编译时期进行类型检查。

当使用get方法获取泛型类中的元素时,由于编译器在编译时期已经推断出了泛型类型,因此编译器会在方法调用时自动进行强制类型转换,将Object类型转换为具体的泛型类型。

编译前

public class Pair<T> {
    
    public T value;

    public T getValue() {
        return value;
    }
}

编译后

public class Pair {
    
    public Object value;

    public Object getValue() {
        return value;
    }
}




7 - 使用泛型要注意

7.1 - 泛型不能是基本数据类型

基本数据类型不能传入泛型参数中,但是包装类可以。

7.2 - 重载方法

public class Temp  {

    public void print(Dog<Teddy> teddy) {

    }

    public void print(Dog<Koki> b) {

    }
}

因为在编译的时候,泛型会被擦除。所以编译的时候会变成下面的代码

public void print(Dog teddy) {
}
public void print(Dog koki) {
}

类型擦除后的重载方法,就变成一样的方法,所以会报错




7.3 - 类型擦除和多态的冲突

使用桥接方法
B站链接

7.4 - 静态变量和静态方法

public class Dog<T> {

    // 编译报错
    public static T value;
    
    // 泛型方法 成功
    public static <T> T temp(T value) {
        return value;
    }
}

静态变量:因为泛型类只有被创建的时候才知道是什么类型,而加载类的时候静态变量会在创建类之前就已经加载了,所以不知道是什么类型就会报错。

静态方法:因为在调用传参的时候就明确了具体的类型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

18岁_老大爷

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

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

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

打赏作者

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

抵扣说明:

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

余额充值