java--泛型

21 篇文章 0 订阅
1 篇文章 0 订阅

泛型的类型变量不可以是基本类型

泛型的类型变量都是引用类型

泛型使用之前

在面向对象的世界中,多态算是一种泛化机制,但是它是建立在继承之上的。

            List list = new ArrayList<>();
            list.add("laoqiang");
            list.add(1);
            String s = (String)list.get(0);
            System.out.println(s);
            int a = (int)list.get(1);
            System.out.println(a);
            String s1 = (String)list.get(1);
            System.out.println(s1);

这里写图片描述

存在的缺陷:

  1. 当我们获取一个值的时候,必须进行强制类型转换。
  2. 如果向集合中添加了非预期的类型(如Integer),编译时我们不会收到任何的错误提示。但当我们运行程序时却会报异常。
泛型参数

为了解决上面的问题,我们对集合采用了泛型参数,编译器会自动帮我们检查,避免向集合中插入错误类型的对象,从而使得程序具有更好的安全性。

泛型的类型擦除
            List<String> list = new ArrayList<>();
            List<Integer> list1  = new ArrayList<>();
            System.out.println(list.getClass()==list1.getClass());//true

这是为什么呢,明明我们定义了两种不同的类型?因为,在编译期间,所有的泛型信息都会被擦除,List和List类型,在编译后都会变成List类型(原始类型)。Java中的泛型基本上都是在编译器这个层次来实现的,这也是Java的泛型被称为“伪泛型”的原因。由上面的这段话,也是可以看出泛型也就是在编译的时候,出来吓吓人,运行期就怂了。

反射添加非预期的类型
List<String> list = new ArrayList<String>();
            try {
                list.getClass().getMethod("add", Object.class).invoke(list, 111);
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
             for (int i=0;i<list.size();i++) {    
                    System.out.println(list.get(i));    
                }    

上面的代码可以成功添加进去,这说明ArrayList泛型信息在编译之后被擦除了,只保留了原始类型,类型变量(T)被替换为Object,在运行时,我们可以行其中插入任意类型的对象。但是无法去遍历出来,会报异常。

泛型方法的类型推断
List<String> list = new ArrayList<String>();
List<String> list = new ArrayList();
List list = new ArrayList<String>();

我们在使用集合的时候,经常看到这两种使用泛型的方式,有啥不同吗,这里面就涉及到泛型的推断机制,第一种肯定不要说,你肯定明白,但是第二种,它是通过推断得出的,泛型推断在jdk 1.7之前,是没有的,所以在1.7前,如果是第二种方法,编译会提出警告。第三种,这种是没有泛型的,不要看见它的后面有泛型。

  • 在调用泛型方法的时候,可以指定泛型类型,也可以不指定。
  • 在不指定泛型类型的情况下,泛型类型为该方法中的几种参数类型的共同父类的最小级,直到Object。
  • 在指定泛型类型的时候,该方法中的所有参数类型必须是该泛型类型或者其子类。
        int c = Test241.add(1, 2);
        Number d = Test241.add(1, 1.2);//这两个参数一个是Integer,另一个是Float,所以取同一父类的最小级,为Number 
        Object c1  = Test241.add("1sdsa", 1);//这两个参数一个是Integer,另一个是String,所以取同一父类的最小级,为Object
        Integer a = Test241.<Integer>add(1, 2);//指定了Integer,所以只能为Integer类型或者其子类   
        String e = Test241.<String>add("sdas", "sds");
public static <T>(申明的泛型T必须在返回值后面) T add (T a,T b) {//方法的泛型,先申明T为泛型,注意上面方法泛型的调用。
            return a;
        }
泛型引用传递
            List<String> list= new ArrayList<Object>();//编译错误

//等价转换于
    private static List<String> list = new ArrayList<String>();
    private static List<Object> list1 = new ArrayList<Object>();

    list1.add("eeee");
        list1.add(2);
        list = list1;//编译出错
        你想想泛型的出现,就是为了解决类型转换,它是指定精确的类型,我去你这Object,里面啥都有,怎么精确转换
            //list都是String类型的对象,可是它里面实际上已经被我们存放了Object类型的对象,这样,就会有ClassCastException了。
            List<Object> list1 = new ArrayList<String>();
//等价转化于
    private static List<String> list = new ArrayList<String>();
    private static List<Object> list1 = new ArrayList<Object>();

//编译错误,这种就相当与你添加String类型,它把你转成Object,这不是和泛型初衷违背了。

所以:在泛型中,前面的泛型必须一致。
泛型实例在运行时查询
            if(list instanceof ArrayList<String>) {//这种是不正确的

            }

java限定了这种类型查询的方式,?为通配符,也即非限定符。

    private static List<String> list = new ArrayList<String>();
    private static List<Object> list1 = new ArrayList<Object>();
        if(list instanceof List<?>) {
            System.out.println("1111");
        }
        if(list1 instanceof List<?>) {
            System.out.println("222");
        }

因为类型擦除之后,只剩下原始类型,泛型信息不存在了。也就是说你能不能判断集合是不是List的实例。

            List<String> list= new ArrayList<String>();
            if(list instanceof ArrayList<?>) {

            }
泛型的静态使用注意点
public class Test241<T> {
    private static T one;//编译出错,你要知道static变量是不需要构造对象调用的,对象都没有创建,你怎么执行泛型类的类型。

}

因为泛型类中的泛型参数的实例化是在定义泛型类型对象。

public static <T> T show(T a) {//你可以编译通过一个static的泛型方法,因为这个泛型方法的泛型是在你在调用静态方法指定的。
        return a;
    }

    Test241.<String>show("aaa");
泛型中?与T区别
  1. T 声明一个泛型类或泛型方法,T是指定的一个死的类型。
  2. ?使用泛型类或泛型方法,而?是随便啦,可以变化的。

参考:这里写链接内容

public class Test241<T> {

    List<?> list = new ArrayList<T>();//T代表一种类型,你可以赋给多个类型的
    //List<T> list1 = new ArrayList<?>();//编译出错,你想想,?可以代表多种类型,而你的T只是一种类型,肯定不行
    public static void main(String[] args) {
        //List<String> list = new ArrayList<String>();
        Test241<?> l = new Test241<>();//在类名你已经指定是泛型,在这里,你要使用,但是你却不指定它具体的类型
    }
}
泛型中的非限定通配符 ?

一种是

使用泛型的占位符

编写泛型方法并不困难,你需要用泛型类型来替代原始类型,比如使用T, E or K,V等被广泛认可的类型占位符。

  public  <K,V> V  put( K key, V value) {  
        return null;  
    }  
    public <E> void  show(E e) {

    }

注意申明在方法中申明泛型占位符,一定要在返回值之前,先记住吧!!!

数组中是否可以使用泛型

答案是否定的,在书中建议我们去使用集合,取代数组,因为他可以在编译期提供安全。

List和原始类型List之间的区别

原始类型和带泛型参数类型之间的主要区别是,在编译时编译器不会对原始类型进行类型安全检查,还有就是带泛型参数的不可以将集合引用直接给一个集合,会出现编译错误。

        List list = new ArrayList();
        List<Object> list1 = new ArrayList<Object>();
        List<String> list2 = new ArrayList<String>();
        list1 = list2;//编译出错,这个问题可以看作泛型的传递的问题
        list = list2;
        list1.add(list2);//可以正常添加
        list.add(list2);//可以正常添加
Java中List
泛型的通配符上界与下界

实际上这种可以称之为限定符,这表示泛型T是受限制

  List<? extends T> list = new ArrayList();//这个泛型表示可以接受T的子类,作为泛型参数。
       list.add(1);//编译失败
       list.add("sdsd");//编译失败
       list.add(null);//编译成功

带有这种泛型通配符不可以添加任何类型,只可以为空。

这种限定泛型,还可以规定该泛型类必须实现某个接口

public static  <T extends Comparable<T>> void sort(T[] s) {
        for(int i = 0;i<s.length;i++) {
            for(int j = i+1;j<s.length;j++) {

                if(s[i].compareTo(s[j])>0){
                T temp;
                temp = s[i];
                s[i]=s[j];
                s[j] = temp;
                }
            }
        }
    }

这时我们手写一个给对象排序的方法,这里主要不是看排序,我们要保证传入的类型T是已经实现Compable接口的。

参考学习:这里写链接内容

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值