【JavaSE】泛型

目录

引入

泛型类

泛型方法

擦除机制

泛型的上界

通配符 

通配符的上界

通配符的下届


一般的类和方法都使用的是具体的类型。但是有了泛型这个语法后,泛型类和泛型方法就可以使用各种各样的类型。泛型从本质上来说就是对类型实现了参数化。


引入

class Array{
    private Object[] arr = new Object[10];

    public void setPos(int pos, Object val){
        arr[pos] = val;
    }
    public Object getPos(int pos){
        return arr[pos];
    }
}

public class Test {
    public static void main(String[] args) {
        Array arr = new Array();
        //Object类为所有类型的父类,这里发生了向上转型
        arr.setPos(0, "hello");
        arr.setPos(1, 2);
        arr.setPos(3, 1.5);

        //得到里面的数据时,每次必须强转才可以
        String str = (String)arr.getPos(0);
        System.out.println(str);
        int a = (int)arr.getPos(1);
        System.out.println(a);
    }
}

这里的数组arr可以存各种数据,但是使用其数据时是不方便的。这就要用到泛型这一语法来改造上述代码了。泛型可以自动检查数据是否为自己想要的数据类型,使用时会自动强转。


泛型类

语法

class ClassName<T, T2, T3>{
    //类中可以使用T, T2, T3 等类型参数
}

 

<T>代表占位符,表示当前的类为泛型类

一般的:

E 表示 Element
K 表示 Key
V 表示 Value
N 表示 Number
T 表示 Type
S, U, V 等等 - 第二、第三、第四个类型
class Array<T> {
    //不能new泛型的数组
    //private T[] arr = new T[10];

    //这种写法可以达到目的,但也不是最优解
    private T[] arr = (T[]) new Object[10];

    //最好通过反射来创建

    public void setPos(int pos, T val){
        arr[pos] = val;
    }
    public T getPos(int pos){
        return arr[pos];
    }
}

public class Test {
        public static void main(String[] args) {
        //这里我想存一组整数
        //<>中不能为基本数据类型 如int、float等
        //只能为对应的包装类型或其他类
        //Array<int> arr = new Array();
        Array<Integer> arr = new Array();

        //可以省略后面实参Integer,编译器会与前面的形参匹配出后面的实参
        //Array<Integer> arr = new Array<Integer>();

        //自动检查,非整数的类型不能存放
        //arr.setPos(0, "hello");
        //arr.setPos(3, 1.5);

        arr.setPos(1, 2);
        //不需要强转了
        int a = arr.getPos(1);
        System.out.println(a);
    }
}

泛型方法

一般方法

class Fun<T> {
    //这是一个一般方法,需要实例化出对象才可以使用
    //在返回类型前加  <参数类型列表>
    public <T> void Swap(T[] arr, int a, int b){
        T t = arr[a];
        arr[a] = arr[b];
        arr[b] = t;
    }
}

public class Test {
    public static void main(String[] args) {
        //实例化出f
        Fun<Integer> f = new Fun<Integer>();
        Integer[] arr = {4, 7};
        //使用一般方法
        f.Swap(arr, 0, 1);
    }
}

静态方法

class Fun{
    //这是一个静态方法,不需要实例话对象,直接调用类方法即可
    //在返回类型前加  <参数类型列表>  只不过多了static
    public static <T> void Swap(T[] arr, int a, int b){
        T t = arr[a];
        arr[a] = arr[b];
        arr[b] = t;
    }
}

public class Test {
    public static void main(String[] args) {
        Integer[] arr = {4, 7};
        Fun.Swap(arr, 0, 1);
    }
}

擦除机制

擦除机制是泛型当中发生在编译过程中的机制。它是把所有泛型的类型都替换成了Object(在没有设上界的时候)

所以编译器生成的字节码文件中没有泛型的类型,只有Object类。


泛型的上界

class ClassName<T extends 父类/接口>{

}

1. 泛型的好处是用户可以自己随便使用类型,但是有了泛型的上界后,用户只能使用父类或者其子类。

2. 没有上界时,擦除成Object类,但是有了上界,就会擦除成父类

3. 如果extends接口后,必须实现该接口,就可以使用接口了。

 比如,当我们extends Number这个父类后,我们只能用其本身或者其子类。

 当上界是父类时:

class TestNumber<T extends Number>{
    public void Swap(T[] arr, int a, int b){
        T t = arr[a];
        arr[a] = arr[b];
        arr[b] = t;
    }
}

public class Test {
    public static void main(String[] args) {
        //Integer是number的子类
        Integer[] arr1 = {1, 2};
        TestNumber<Integer> a1 = new TestNumber<>();
        a1.Swap(arr1, 0, 1);

        //Double是number的子类
        Double[] arr2 = {3.0, 4.0};
        TestNumber<Double> a2 = new TestNumber<>();
        a2.Swap(arr2, 0, 1);

        //String不是number的子类,无法使用
        String[] arr3 = {"hello", "world"};
        TestNumber<String> a3 = new TestNumber<String>();
        a3.Swap(arr3, 0, 1);
    }
}

当上界是接口时:

//此时上界为Comparable<T>这个接口
//因为目的要找最大值,此接口中有两数的比较方法
class Max<T extends Comparable<T>>{
    public T findMax(T[] arr){
        T max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if(max.compareTo(arr[i]) < 0){
                max = arr[i];
            }
        }
        return max;
    }
}

public class Test {
    public static void main(String[] args) {
        Max<Integer> a = new Max<>();
        Integer[] arr = {2, 6, 3, 8, 4};
        Integer max = a.findMax(arr);
        System.out.println(max);
    }
}

 

通配符 

通配符是用来解决泛型无法协变的问题。协变:若Student是Person的子类,则 类名<Student> 也应该是  类名<Person> 的子类,但是泛型不支持这样的语法。如下代码。

class TestDemo<T> {
    private T number;

    public T getNumber() {
        return number;
    }

    public void setNumber(T number) {
        this.number = number;
    }

}
public class Test {
    public static void print(TestDemo<String> a) {
        System.out.println(a.getNumber());
    }
    public static void main(String[] args) {
        TestDemo<String> s = new TestDemo<>();
        s.setNumber("这是为了测试通配符");
        print(s);
    }
}

 

class TestDemo<T> {
    private T number;

    public T getNumber() {
        return number;
    }

    public void setNumber(T number) {
        this.number = number;
    }

}
public class Test {
    //这里print方法只能打印String类的,当变成Integer类时就报错了
    public static void print(TestDemo<String> a) {
        System.out.println(a.getNumber());
    }

    public static void main(String[] args) {
        TestDemo<Integer> s = new TestDemo<>();
        s.setNumber(233);
        print(s);
    }
}

对于print方法的解决方案就是使用通配符   ?

public static void print(TestDemo<?> a) {
    System.out.println(a.getNumber());
}

 这样不管是String还是Integer类,它都可以打印。

通配符的上界

<? extends 父类>
//这里可以传入父类的子类或父类本身
class TestDemo<T>{
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}

public class Test {
    //通配符的上界只能接受任意类型,不能修改
    //因为修改要有明确的类型,如果不知道,那么就不能修改
    //在编译的时候已经擦除成 TestDemo<Number> 了
    public static void fun(TestDemo<? extends Number> x){
        //可以接受,相当于发生向上转型
        Number i = x.getT();
        //x.setT(4);
    }
    public static void main(String[] args) {
        TestDemo<Integer> a = new TestDemo<>();
        a.setT(2);
        fun(a);
    }
}

通配符的下届

<? super 子类>
//这里可以传入子类的父类或子类本身

 

class TestDemo<T>{
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}

public class Test {

    public static void fun(TestDemo<? super Integer> x){
        //通配符的下届可以修改,不能接收
        //被擦除成 TestDemo<Integer>
        x.setT(4);

        //不能确定是哪个父类,所以无法接受
        //Integer i = x.getT();

    }
    public static void main(String[] args) {
        TestDemo<Number> a = new TestDemo<>();
        a.setT(2);
        fun(a);
    }
}

有什么错误评论区指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值