泛型与泛型进阶

泛型

//不能new泛型类型的数组
T[] t = new T[5];//这是错误的

//但是可以new一个Object的,然后强制类型转换,但是这样子并不好
T[] t = (T[])new Object[5];
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 class Test {
    public static void main(String[] args) {
        MyArray<Integer> myArray = new MyArray<Integer>();//后面的这个<Integer>可以省略,叫做类型推倒
        myArray.setVal(0,10);
        myArray.setVal(1,20);
        myArray.setVal(2,30);
        System.out.println(myArray.getPos(1));
    }
}

泛型可以有很多个:

class MyArray<T, E, P> {
    //只要在里面加逗号就行,在构造出来的时候也要对应的有多少种类型。
}
  • 裸类型
MyArray list = new MyArray();

我们不要自己去使用裸类型,裸类型是为了兼容老版本的 API 保留的机制

小结:

  1. 泛型是将数据类型参数化,进行传递
  2. 使用 表示当前类是一个泛型类。
  3. 泛型目前为止的优点:数据类型参数化,编译时自动进行类型检查和转换

官方用法

public Object[] obj = new Object[3];
public E getPos2(int pos) {
    return (E)obj[pos];
}
  • 反射(说明了为什么不能实现泛型类型数组)
import java.util.Arrays;

class MyArray1<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 Object[] getArray() {
        return array;
    }
}

public class Test1 {
    public static void main(String[] args) {
        MyArray1<Integer> myArray = new MyArray1<>();
        myArray.setVal(0,10);
        myArray.setVal(1,20);
        myArray.setVal(2,30);

        Integer[] integer = (Integer[])myArray.getArray();
        System.out.println(Arrays.toString(integer));
    }
}
//Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;

原因:替换后的方法为:将Object[]分配给Integer[]引用,程序报错

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

需要改为:

import java.lang.reflect.Array;
import java.util.Arrays;

class MyArray1<T> {
    public T[] array;

    public T getPos(int pos) {
        return this.array[pos];
    }

    public void setVal(int pos, T val) {
        this.array[pos] = val;
    }

    public Object[] getArray() {
        return array;
    }

    public MyArray1() {

    }

    public MyArray1(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }
}

public class Test1 {
    public static void main(String[] args) {
        MyArray1<Integer> myArray = new MyArray1<>(Integer.class, 10);
        myArray.setVal(0,10);
        myArray.setVal(1,20);
        myArray.setVal(2,30);

        Integer[] integer = (Integer[])myArray.getArray();
        System.out.println(Arrays.toString(integer));
    }
}
//[10, 20, 30, null, null, null, null, null, null, null]
  • 泛型的上界
class Alg<E extends Comparable<E>> {
    public E findMax(E[] array) {
        E max = array[0];
        for(int i = 1; i < array.length;i++) {
            if(max.compareTo(array[i]) < 0 ) {
                max = array[i];
            }
        }
        return max;
    }
}

泛型方法:

public <E extends Comparable<E>> E findMax(E[] array) {
        E max = array[0];
        for(int i = 1; i < array.length;i++) {
            if(max.compareTo(array[i]) < 0 ) {
                max = array[i];
            }
        }
        return max;
    }

泛型进阶

<>当中的数据类型 不参与类型的组成 JVM当中没有泛型的概念 泛型 只存在于编译阶段,通过擦除机制

在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制

比如我们看一下ArrayList源码中:
在这里插入图片描述它定义了一个Object类型的数组,包括它new的时候都是new一个Object的数组
在这里插入图片描述
那么它是怎么搞成泛型的呢?我们看一下它在add方法的技巧:
在这里插入图片描述在这里插入图片描述
get返回了E类型的,再看一下elementData中的源码:
在这里插入图片描述
我们知道了它把elementData数组的类型强制转换成了E类型的,这种是官方的写法,也是最标准的写法

泛型的上界
public class MyArray<E extends Number> 
//这里的E只能是Number或者是Number的子类,比如Integer就行,而String就不行
public class MyArray<E extends Comparable<E>>
//这里的E必须要是实现了Comparable接口的
通配符
class Message<T> {
    private T massage;

    public T getMassage() {
        return massage;
    }

    public void setMassage(T massage) {
        this.massage = massage;
    }
}

public class Test {
    public static void main(String[] args) {
        Message<String> message = new Message<>();
        message.setMassage("欢迎进入字节跳动!");
        func(message);

        Message<Integer> message1 = new Message<>();
        message1.setMassage(111);
        func(message1);
    }

    private static void func(Message<?> message) {//假设这里没有用通配符而是用泛型,在message1                                                     会报错,因为指定了用String
        //message.setMassage("字节跳动"); 类型不能确定,无法设置 无法修改
        System.out.println(message.getMassage());
    }
}
  • 通配符的上界
<? extends 上界>
<? extends Number>//可以传入的实参类型是Number或者Number的子类
<? super 下界>
<? super Integer>//代表 可以传入的实参的类型是Integer或者Integer的父类类型
class Food {
}
class Fruit extends Food {
}
class Apple extends Fruit {
}
class Banana extends Fruit {
}
class Plate<T> {
    private T message;

    public T getMessage() {
        return message;
    }
    public void setMessage(T message) {
        this.message = message;
    }

}
public class Test1 {

    public static void main(String[] args) {
        Plate<Fruit> fruitPlate = new Plate<>();
        fruitPlate.setMessage(new Fruit());
        func2(fruitPlate);

        Plate<Food> foodPlate = new Plate<>();
        foodPlate.setMessage(new Food());
        func2(foodPlate);
    }

    //泛型的下界:传进来的是Fruit和Fruit的父类,所以你既然传了父类或者它自己
    //那么它的子类就可以进行修改
    public static void func2(Plate<? super Fruit > tmp) {
        tmp.setMessage(new Apple());
        tmp.setMessage(new Banana());
        //Fruit fruit = tmp.getMessage(); 这里因为你不知道传进来的是哪个父类,所以不能接收,只能直接打印结果
        System.out.println(tmp.getMessage());
    }

    public static void main1(String[] args) {
        Plate<Apple> applePlate = new Plate<>();
        applePlate.setMessage(new Apple());
        func1(applePlate);

        Plate<Banana> bananaPlate = new Plate<>();
        bananaPlate.setMessage(new Banana());
        func1(bananaPlate);
    }

    //泛型的上界:传进来的可以是Fruit和Fruit的子类,所以下面那里不知道传进来的是Apple还是Banana等
    //所以不能进行修改
    public static void func1(Plate<? extends Fruit> tmp) {
        //这里仍然不可以进行修改!

        Fruit fruit = tmp.getMessage();
        System.out.println(tmp.getMessage());
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值