java之泛型

泛型

​ 每次创建一个集合,我们都需要指定这个集合里头能放什么元素,而放其他元素就会classcastexception;为了解决这个问题,我们可以创建类型为Object的集合,但是此时虽然我们什么类型都可以放了,但是正由于什么类型都能放,第一:元素会很乱;第二:用指定类型的变量接收集合返回的数据时,我们还需要进行强制类型转换,很麻烦.

泛型类❓

  1. 语法:
class 类名 <泛型标识符,泛型标识符...>{
    private 泛型标识 变量名;
    ...
}

常见的泛型标识:T/E/K/V


2:使用语法(利用泛型类实例化对象)

类名<具体的数据类型> 对象名=new 类名<具体的数据类型>();
//jdk1.7之后,后面的<>里的类型可省略不写

3:注意事项

  1. 泛型类在创建对象时,如果没有指定具体类型,将按Object类型来操作
  2. 泛型类不支持基本数据类型(int char…)
  3. 同一个泛型类,根据不同的数据类型所穿件的对象,本质是同一个类型
public class Test {
    public static void main(String[] args) {
        ArrayList<String> arrayList1=new ArrayList<>();
        ArrayList<Integer> arrayList2=new ArrayList<>();

        /**
         * 一般情况下,getclass()方法和class()方法是等价的
         * 两者最直接的区别就是,getClass()是一个类的实例所具备的方法
         * 而class()方法是一个类的方法。
         * 另外getClass()是在运行时才确定的
         * 而class()方法是在编译时就确定了。
         */
        System.out.println(arrayList1.getClass() == arrayList2.getClass());//打印true
    }
}

使用泛型类实现一个小案例💎

class ProductGetter<T>{
    Random random=new Random();

    //奖品
    private T product;
    //奖池
    private ArrayList<T> list=new ArrayList<>();
    //添加奖品
    public void addProduct(T t){
        list.add(t);
    }
    //抽奖
    public T getProduct(){
        int index=random.nextInt(list.size());
        return list.get(index);
    }
}
public class Test {
    public static void main(String[] args) {
        ProductGetter<String> gift=new ProductGetter<>();
        String[] gifts={"苹果手机","小米手环","扫地机器人","500元现金"};
        //为奖池添加奖品
        for(int i=0;i<4;i++){
            gift.addProduct(gifts[i]);
        }
        //开始抽奖
        String ret=gift.getProduct();
        System.out.println(ret);
    }
}
  1. 实例化泛型类时,将类型作为了参数,即类型参数化
  2. 从该集合中获取到的元素,不需要像Object集合那样,每次都要强转,这里编译器可自动为我们完成强转

泛型类派生子类🍢

  1. 子类也是泛型类,子类和父类的泛型类型要一致;即使子类的泛型标识有多个,也要保证子类的泛型标识中至少有一个和父类的泛型标识一致

    class child<T> extends parent<T>
    
  2. 子类不是泛型类,那此时该子类继承一个泛型类时,就要明确想继承什么类型的父类了

    class child extends parent<String>
    

泛型接口🍰

  1. 接口注定是被实现类去实现的,所以实现类具体是不是泛型类,并且有什么要求,和泛型类派生子类是一样的规定

  2. 语法

    interface 接口名称 <泛型标识符,泛型标识符...>{
        泛型标识 方法名();//自带public abstarct 修饰的抽象方法
    }
    
  3. 具体

    interface Generator<T>{
        T getKey();
    }
    class Apple implements Generator<String>{
        //重写的方法,从泛型集合中获取不需要强转,说明类型就是我们所指定的String,所以为了
        //形成重写,这里的返回值类型应当写成我们所指定的类型
        public String getKey(){
            return "hello generic";
        }
    }
    
    interface Parent<T>{
        T getKey();
    }
    //保证至少一个相同,是因为构造子类对象时,类型参数的其中若干个也可以为泛型父类提供类型参数信息,进而构造出具体的父类
    class child<T,E> implements Parent<T>{
        private T key;
        private E value;
    
        public child(T key, E value) {
            this.key = key;
            this.value = value;
        }
    
        @Override
        public T getKey() {
            return key;
        }
        
        public E getValue(){
            return value;
        }
    }
    

泛型方法⚡️

在调用方法的时候,才会指明这个方法带有的泛型标识代表的具体是什么类型.

  1. 语法:

    修饰符 <泛型标识,泛型标识...> 返回值类型 方法名(形参列表){
        方法体;
    }
    
  2. 必须要求有<泛型标识>存在,否则不叫泛型方法,要区别于前述泛型类里定义的带有泛型标识的方法,但是没带<>,所以它们都不是泛型方法.

  3. 泛型标识也可以随便写,比如:T/E/V/K等

  4. 使用

    class ProductGetter<T>{
        Random random=new Random();
    
        //奖品
        private T product;
        //奖池
        private ArrayList<T> list=new ArrayList<>();
        //添加奖品
        public void addProduct(T t){
            list.add(t);
        }
        //抽奖
        //泛型方法
        public <E> E getProduct(ArrayList<E> list){
            int index=random.nextInt(list.size());
            return list.get(index);
        }
    }
    public class Test {
        public static void main(String[] args) {
            ArrayList<String> list1=new ArrayList<>();
            ArrayList<Integer> list2=new ArrayList<>();
            
            list1.add("苹果手机");
            list1.add("扫地机器人");
            list1.add("小米手环");
            list1.add("500元现金");
            
            list2.add(1000);
            list2.add(2000);
            list2.add(3000);
            
            ProductGetter<String> gift=new ProductGetter<>();
            String product = gift.getProduct(list1);
            System.out.println(product);
    
            System.out.println(gift.getProduct(list2));
        }
    }
    

    可以看出,使用泛型方法,就不用先填充奖池,再抽奖了,其次可以发现,泛型方法使用的类型可以独立于泛型类的指定类型.还要知道,普通类里也能写泛型方法!

  5. 注意事项

    1. 只是使用了泛型标识的普通方法,不能被static修饰
    2. 泛型方法可以被static修饰
    3. 泛型方法可以定义在普通类中
    4. 可以了解一下可变参数的泛型方法的使用

类型通配符❎

  1. 类型通配符一般用 ? 表示
  2. 类型通配符代表的是类型实参,不是类型形参
  3. 思考一个问题:考虑为什么box2传参已经编译出错了!?
class Box<T>{
    private T first;

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }
}
public class Test2 {
    public static void main(String[] args) {
        Box<Number> box1=new Box<>();
        box1.setFirst(100);
        showBox(box1);
        
        Box<Integer> box2=new Box<>();
        box2.setFirst(200);
        showBox(box2);

    }
    private static void showBox(Box<Number> box){
        Number first=box.getFirst();
        System.out.println(first);
    }
}

答:box2和showBox的形参类型不匹配.我们一开始的思想可能想用向上转型去理解,但是这里并不构成父子关系.那如何改进,看下面这个方法可行?

class Box<T>{
    private T first;

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }
}
public class Test2 {
    public static void main(String[] args) {
        Box<Number> box1=new Box<>();
        box1.setFirst(100);
        showBox(box1);

        Box<Integer> box2=new Box<>();
        box2.setFirst(200);
        showBox(box2);

    }
    private static void showBox(Box<Number> box){
        Number first=box.getFirst();
        System.out.println(first);
    }
    private static void showBox(Box<Integer> box){
        Number first=box.getFirst();
        System.out.println(first);
    }
}

答:改成上述写法,可能我们的想法是既然向上转型不可行,那我利用方法的重载总可以了吧!?结果是否定的,重载的要求是:返回值情况不做要求,函数名相同,形参列表不同,但是,这里的形参列表本质是相同的,所以并没有构成方法的重载.那往下引出类型通配符

class Box<T>{
    private T first;

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }
}
public class Test2 {
    public static void main(String[] args) {
        Box<Number> box1=new Box<>();
        box1.setFirst(100);
        showBox(box1);

        Box<Integer> box2=new Box<>();
        box2.setFirst(200);
        showBox(box2);

    }
    private static void showBox(Box<?> box){
        Object first=box.getFirst();
        System.out.println(first);
    }
}

这时可正常打印了;也就是说:指定了类型的泛型类类型作为形参,实参必须是与形参一致;其次通配符的出现,可允许实参被指定各种类型.


通配符的上限💅

/接口 <? extends 实参类型>

要求该泛型的类型,只能是实参类型或者实参类型的子类类型.

所以可以将上述代码改成:

class Box<T>{
    private T first;

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }
}
public class Test2 {
    public static void main(String[] args) {
        Box<Number> box1=new Box<>();
        box1.setFirst(100);
        showBox(box1);

        Box<Integer> box2=new Box<>();
        box2.setFirst(200);
        showBox(box2);

    }
    private static void showBox(Box<? extends Number> box){
        Number first=box.getFirst();//Number作为了上限,用这个类型接收,即使向上转型也没关系
        System.out.println(first);
    }
}

通配符的下限Ⓜ️

/接口 <? super 实参类型>

要求该泛型的类型,只能是实参类型,或者实参类型的父类类型

所以上述代码可以改成:

class Box<T>{
    private T first;

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }
}
public class Test2 {
    public static void main(String[] args) {
        Box<Number> box1=new Box<>();
        box1.setFirst(100);
        showBox(box1);

        Box<Integer> box2=new Box<>();
        box2.setFirst(200);
        showBox(box2);

    }
    private static void showBox(Box<? super Integer> box){
        Object first=box.getFirst();//上限不定,只能用Object接收了
        System.out.println(first);
    }
}

类型擦除✌️

jdk1.5引入的,泛型代码能够很好的与之前的版本代码兼容,这是因为泛型信息只是存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除,称类型擦除.

  1. 无限制的类型擦除

    public class Erasure<T>{
        private T key;
        public T getKey(){
            return key;
        }
        public void setKey(T key){
            this.key=key;
        }
    }
    //擦除完:
    public class Erasure{
        private Object key;
        public Object getKey(){
            return key;
        }
        public void setKey(Object key){
            this.key=key;
        }
    }
    
  2. 有限制的类型擦除

    就是说泛型类带了上限,此时就是把上面的Object擦成上限类型即可.

  3. 泛型方法的类型擦除

    与泛型类擦除方法一致.

泛型数组📦

  1. 可以申明带泛型的数组引用,但不能直接实例化一个泛型数组
  2. 可以通过java.lang.reflect.Array的newInstance(Class ,int)来创建T[]数组
  3. 后续介绍反射时,再重点介绍泛型数组的构建
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值