java泛型

java泛型

1、为什么需要泛型?

(1)在编译的时候提供安全检测的功能

(2)同时能支持多种类型参数。

概念有点抽象,举个例子比如List 我们在使用的时候,可以指定不同的类型。

    List<String> strList=new ArrayList<>();
    List<Integer> intList=new ArrayList<>();

为什么能支持这样呢,看ArrayList的源码,可以看到E就是不确定的类型,需要外部指定。

    public class ArrayList<E> extends AbstractList<E>{
            
    }

那它又如何进行安全检测呢。比如此时给intList添加一个字符串类型的元素。此时编译器会标红报错,提醒类型错误。

    list2.add("111");

2、泛型的分类

泛型可以分为三种,分别是泛型类,泛型接口,泛型方法。

ArrayList就是一个典型的范型类。这里自定义一个泛型类。

    public class Generic<T>{ //T可以换成任意字母 
        private T key;
    
        public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
            this.key = key;
        }
    
        public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
            return key;
        }
    }

当外部创建这个类传入指定类型时,这个类中的所有T类型会被自动替换成该类型。如下例子所示,将这个Generic中的T类型转化成了String类型。

    Generic<String> generic=new Generic<>("11");

泛型接口,典型例子就是List,可以看到源码里是这样写的。

    public interface List<E> extends Collection<E>

这里再自定义一个泛型接口。

    public interface Generator<T> {
        public T next();
    }

再创建一个类实现这个接口

有两种方式

1)实现类没指定具体类型时,在声明时需要包含泛型

    class FruitGenerator<T> implements Generator<T>{
        @Override
        public T next() {
            return null;
        }
    }

2)实现类中指定了接口的类型。

    public class FruitGenerator implements Generator<String> {
    
        private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
    
        @Override
        public String next() {
            Random rand = new Random();
            return fruits[rand.nextInt(3)];
        }
    }

类型通配符

比如有一个方法

    public void myFun(List<String> list){
    }

这时候很明显,只能传List<String>的,而不能传List<Integer>类型。如果我不想指定某一个具体类型时,可以借助类型通配符?。

    public void myFun(List<?> list){
    }

这样就可以传入不同类型的List而不会报错了。

    myFun(new ArrayList<String>());
    myFun(new ArrayList<Integer>());

泛型方法

并不是泛型类中包含了泛型符号的方法就是泛型方法,如下所示的方法就不是泛型方法。

    public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
       this.key = key;
    }
    
    public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
       return key;
    }

真正的泛型方法需要 在返回值和权限修饰符中用<T>对方法表明是一个泛型方法。如下所示只有包含在<>中的才能当作泛型符号使用,并且可以同时使用多个。

    public <T> T myFun(T object) {
        return object;
    }

泛型方法和可变参数

泛型方法还可以不指定形参的具体个数,通过...代表可变参数 。如下所示,此时arr相当于数组。

    public <T> void  myFun(T... arr) {
        for (T t:arr){
            System.out.println(t);
        }
    }

3、泛型的限定

(1)  作用:为了限定只能传入某种类型的父类或者某种类型的子类。

(2)为泛型添加上边界,即传入的类型实参必须时指定类型的子类型。如下例所示。

    public void myFun(Generic<? extends Number> obj){
    }

    myFun(new ArrayList<Integer>());
    myFun(new ArrayList<Double>());
    myFun(new ArrayList<Long>());

(4)和<? extends Number>对应的是<? super Number>,指的是必须时Number的父类才能传入。

(5)Extends 后面可以是类也可以是接口,可以有多个通过&连接。但如果同时有接口或者类,类有且只能有一个,必须在第一个。

    T,V extends ArrayList&Comparable 

4、泛型数组

如何实现一个泛型数组?

如果直接像这样创建是不行的,编译器会提示错误,会提示Generic array creation。

            Observable<String>[] arr=new Observable<String>[10];

但是如果通过通配符写,则可以。

                    Observable<?>[] arr=new Observable<?>[10];

5、泛型的限制

1  泛型不能实例化  如T a=new T();

2  静态变量类型或者静态方法的返回类型不能是泛型,但本身是泛型方法的时候可以

    private T a;
    private static T a;//报错
    private static  T fun(T a){//报错
            return a;
    }
    private static <T> T fun(T a){//本身是泛型方法时不报错
        return a;
    }

3  泛型不能是基本类型

4  泛型不能使用instanceof

5  泛型的class对象是原生类型,和传入的T类型无关

    System.out.println(restrict.getClass()==restrictString.getClass());
    System.out.println(restrict.getClass().getName());
    System.out.println(restrictString.getClass().getName());

6 泛型类不能创建数组

7 泛型类不能继承自Throwable和Exception也不能被try catch捕获

6 泛型的继承规则

1 假定Worker继承自Employee,那么Pair<Employee>和Pair<Worker>没有任何继承关系

2 泛型类之间可以继承,比如List和ArrayList

7 通配符

通配符用?表示,和T的作用类似,用于安全地访问数据。

区别在于:?只能用在方法上,而T可以在方法、类上

<? extends Worker>不能传入类型 只能取出Worker类型  协变

<? super Employee> 能输入类型,但只能传出object类型。逆变

如下所示 往GenericType<? extends Fruit>内传Fruit和它子类都会报错,注意和 GenericType<Fruit>的区别,这种方式可以直接传入子类。

 原因是<? extends Fruit>未确定指定泛型是哪一种类型,如 GenericType<Fruit>确定指定了泛型是Fruit,因为<? extends Fruit>未指定类型,所以编译器为了安全考虑,会禁止传入类型,而只允许读取值。且读取时,只能知道能够存入  GenericType<? extends Fruit>都是Fruit的子类,因此用Fruit及其父类接受都是安全的。

            GenericType<? extends Fruit> fruitGenericType = new GenericType<>();
          
                    fruitGenericType.setData(new Apple());  //编译错误
                    Apple apple=fruitGenericType.getData();//编译错误
                    Fruit fruit=fruitGenericType.getData();//编译正确
            GenericType<Fruit> fruitGenericType2 = new GenericType<>();
    
            fruitGenericType2.setData(new Apple());

详情见https://blog.csdn.net/zy_jibai/article/details/90082239

8泛型擦除

List<T>在运行时,会被擦除类型成为List<Object>原生类型。因此strList.getClass()==intList.getClass()总是相等的。所有的泛型都会被编译成一个class文件。编译器在Java虚拟机内通过signature属性,在字节码内记录保存相关的泛型信息,因此泛型类型是弱记忆的,不是完全擦除。
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值