我丢,去面试初级Java开发岗位,被问到泛型?

1、泛型的基础概念

1.1 为什么需要泛型

1 List list = new ArrayList();//默认类型是Object 2 list.add("A123"); 3 list.add("B234"); 4 list.add("C345"); 5 System.out.println(list); 6 7 for(int i=0;i<list.size();i++){ 8 //若要将list中的元素赋给String变量,需要进行类型转换,不然会报Incompatible types错误,显示list.get(i)返回的是Object 9 String str = (String) list.get(i); 10 System.out.println(str); 11 } 1213 list.add(123);//因为类型是Object,我们可以把Integer类型或者其他数据类型的元素也加入list之中 14 System.out.println(list.get(3)); 1516 for(int i=0;i<list.size();i++){ 17 //String str = (String) list.get(i);//但是在这里会报错java.lang.ClassCastException,我们不能直接将Integer类型的数据转换成String 18 System.out.println(list.get(i).getClass()); 19 }

如代码中所示,当我们定义了一个List,list默认的类型是所有对象的基类Object,那么我们取出数据的时候需要经过一次类型转换才能进行对象的实际类型的相关操作。因为List中的类型是Object,那么我们先添加了String类型的数据,然后再添加Integer或者其他类型的数据也是允许的,因为编译时List中是Object类型的数据,然而运行的时候却是它本身的类型,所以当我们将List中的数据当作String处理时会抛出java.lang.ClassCastException

那么有没有什么办法可以使集合能够记住集合内元素各类型,且能够达到只要编译时不出现问题,运行时就不会出现java.lang.ClassCastException异常呢?答案就是使用泛型。

1.2 什么是泛型

Java泛型设计原则是:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常。

泛型,即“参数化类型”,把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊类型,把<数据类型>当作是参数一样传递。

相关术语:

  • ArrayList<E>中的E称为类型参数变量

  • ArrayList<Integer>中的Integer称为实际类型参数

  • 整个称为ArrayList<E>泛型类型

  • 整个ArrayList<Integer>称为参数化的类型ParameterizedType

泛型的作用:

  • 代码更加简洁【不用强制转换】

  • 程序更加健壮【只要编译时期没有警告,那么运行时就不会抛出ClassCastException的异常】

  • 可读性和稳定性【在编写集合的时候,就限定了类型】

1 List<String> strlist = new ArrayList<String>(); 2 List<Integer> intlist = new ArrayList<Integer>(); 34 strlist.add("A"); 5 strlist.add("B"); 6 //strlist.add(123);//编译时报错 78 for(String str:strlist){ 9 System.out.println(str); 10 //A 11 //B 12 } 13//加入Java开发交流君样:756584822一起吹水聊天 14 System.out.println(strlist.getClass());//class java.util.ArrayList 15 System.out.println(intlist.getClass());//class java.util.ArrayList 16 System.out.println(strlist.getClass()==intlist.getClass());//true

泛型擦除

泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛型的java程序后生成的class文件中将不再带有泛型信息,以此使程序的运行效率不受到影响,这个过程称之为“擦除”。

泛型这个概念是JDK5提出的,JDK5以前的版本是没有泛型的,需要建通JDK5以下的集合。当把带有泛型特性的集合赋值给老版本的集合的时候,会把泛型给擦除了,它保留的是类型参数的上限,即Object。而当我们将没有类型参数的集合赋给带类型参数的集合,也不会报错,仅仅是会提示“未经检查的转换(Unchecked assignment)”,甚至也可以将它赋给其他不同类型的带有泛型特性的集合,只是依旧会抛出ClassCastException异常。

1 //类型被擦除了,保留的是类型的上限,String的上限就是Object 2 List list = strlist; 34 List<String> stringList2 = list; 5 List<Integer> intList2 = list;//你也可以把它赋给Integer类型的集合,但是当你把这个集合当成Integer的集合操作的时候,依旧会抛出ClassCastException异常 67 for (Integer i:intList2){//java.lang.ClassCastException 8 System.out.println(i); 9 }

3、泛型的定义和使用

3.1 泛型类\泛型接口

泛型类、泛型接口就是把泛型定义在类或者接口上,在用户使用该类的时候才把类型明确下来。我们常用的集合,List<T>,Map<K,V>,Stack<T>……就是泛型类。在类上定义的泛型,在泛型类的方法、变量中都可以使用。

由于类型参数变量T在java泛型中仅仅是一个占位符,在传递参数之后才能使用,即在完成实例创建之后才能使用,所以在泛型类中,不能定义包含泛型类型的静态变量和静态方法,会报错cannot be referenced from a static context。泛型类中包含泛型类型的变量和方法必须在创建了实例明确了传递的类型参数后才可以使用。

1class Myset<T>{ 2 private T value; 3 //public static T sval;//cannot be referenced from a static context 4 public static int sval2; 5//加入Java开发交流君样:756584822一起吹水聊天 6 public Myset(){ 78 } 910 public Myset(T val){ 11 this.value = val; 12 } 1314 public void setValue(T value) { 15 this.value = value; 16 } 1718 public T getValue() { 19 return value; 20 } 2122 /* public static T getSval(){//cannot be referenced from a static context 23 return sval; 24 }*/ 25} 26 Myset<String> myset = new Myset<>(); 27 myset.setValue("12345"); 28 System.out.println(myset.getValue());//12345 2930 myset = new Myset<>("23"); 31//加入Java开发交流君样:756584822一起吹水聊天 32 System.out.println(myset.getClass());//class liwx.learning.Myset

3.2 泛型方法

1 public static <T> void PrintArray(T [] arr){ 2 System.out.print("["); 3 for(T t:arr){ 4 System.out.print(t+","); 5 } 6 System.out.println("]"); 7 } 8Integer[] a = {1,2,3,4,5,6,7}; 9PrintArray(a);//[1,2,3,4,5,6,7,]

3.3 泛型类的继承

泛型类的子类有两种继承方式

  • 子类不明确泛型类的参数变量,子类也是泛型类

  • 子类明确泛型类的参数变量,子类不是泛型类

1//子类不明确泛型类的参数变量,子类也是泛型类 2class MyChiSet1<T> extends Myset<T>{ 3 public MyChiSet1(){ 45 } 6 public MyChiSet1(T val){ 7 super(val); 8 } 9//加入Java开发交流君样:756584822一起吹水聊天 10} 11//子类明确泛型类的参数变量,子类不是泛型类 12class MyChiSet2 extends Myset<String>{ 13 public MyChiSet2(){ 1415 } 16 public MyChiSet2(String val){ 17 super(val); 18 } 19}

3.4 类型通配符?及其上下限

通配符<?>和类型参数变量<T>的区别是什么?通配符<?>是实参而不是类型形参,而且List<?>在逻辑上是List<Integer>,List<Number>等所有List<具体类型实参>的父类,它的使用比类型形参T更加灵活,但传入的通配符通常进行的是许多于具体类型无关的操作,如果涉及到具体类型相关的操作,以及返回值,还是需要使用泛型方法T。

当我们使用?号通配符的时候,只能调用与对象无关的方法,不能调用对象与类型有关的方法。因为直到外界使用才知道具体的类型是什么。

1//虽然Object是所有类的基类,但是List<Object>在逻辑上与List<Integer>等并没有继承关系,这个方法只能传入List<Object>类型的数据 2 public static void showOList(List<Object> list){ 3 System.out.println(list.size()); 4 } 5 //同理,这个方法只能传入List<Number>类型的数据,并不能传入List<Integer> 6 public static void showList(List<Number> list){ 7 System.out.println(list.size()); 8 }//加入Java开发交流君样:756584822一起吹水聊天 9 //使用通配符,List<?>在逻辑上是所有List<Number>,List<Integer>,List<String>……的父类,可以传递所有List类型的数据,但是不能在List<?>类型的数据进行于具体类型相关的操作,如add 10 public static void showList2(List<?> list){ 11 System.out.println("<?>"); 12 System.out.println(list.size()); 13 } 14 //设置通配符上限,可以传入List<Number及Number的子类> 15 public static void showNumList(List<? extends Number> list){ 16 System.out.println(list.size()); 17 } 18 //设置通配符上限,List<? super Number>只可以传入List<Number及其父类> 19 public static boolean Compare(List<? super Number> list1,List<? super Integer> list2){ 20 return list1.size()>list2.size(); 21 }
1     List<Integer> Intgetlist = new ArrayList<>(); 2 List<Number> Numberlist = new ArrayList<>(); 3 //虽然Number是Integet的父类,但是传入List,它们逻辑上没有了继承关系 4 System.out.println(Intgetlist.getClass()==Numberlist.getClass());//true 5//加入Java开发交流君样:756584822一起吹水聊天 6 //showList(java.util.List<java.lang.Number>) 7 //List<Integer>和List<Number>逻辑上无继承关系,所以无法调用 8 //showList(Intgetlist);//showList(java.util.List<java.lang.Number>)in FXtest cannot be applied to(java.util.List<java.lang.Integer>) 9 showList(Numberlist); 1011 //public static void showList2(List<?> list) 12 //通配符List<?>在逻辑上是所有List<具体参数类型>的父类,方法可以传入其子类类型的数据 13 showList2(Intgetlist); 14 showList2(Numberlist); 1516 // public static void showNumList(List<? extends Number> list) 17 //当设定了通配符上限,只能传入List<Number及其子类>的数据 18 List<String> Stringlist = new ArrayList<>(); 19 showNumList(Intgetlist); 20 showNumList(Numberlist);//加入Java开发交流君样:756584822一起吹水聊天 21 //showNumList(Stringlist);//showNumList(java.util.List<? extends java.lang.Number>)in FXtest cannot be applied to(java.util.List<java.lang.String>) 222324 //public static boolean Compare(List<? super Number> list1,List<? super Integer> list2) 25 //当设定了通配符下限,List<? super Number>只能传入List<Number及其父类>的数据,不能传入子类Integer的List, 26 // 而List<? super Integer>则可以传入其父类Number的List 27 //Compare(Intgetlist,Numberlist); 28 Compare(Numberlist,Intgetlist);

通配符的使用在逻辑上还原了泛型类传入数据类型的参数父类子类的继承关系,同时也可以按照需求设定通配符的上限了下限。

  • List<?>在逻辑上是所有List<具体参数类型>的父类,可对所有List数据进行操作

  • List<? extends Type>设定了通配符的上限,可对所有Type及Type的子类进行操作

  • List<? super Type>设定了通配符的下限,可对所有Type及Type的父类进行操作

在这里插入图片描述

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

KVM调整cpu和内存

一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid

Twitter的分布式自增ID算法snowflake (Java版)

概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移