当前位置: 首页 > 科技观察

Java泛型需要注意的问题

时间:2023-03-20 02:16:51 科技观察

我们都知道Java在1.5引入了泛型机制。泛型的本质是参数化类型,也就是说变量的类型是一个参数,然后在使用的时候指定为具体的类型。泛型可以用于类、接口和方法,使用泛型可以使代码更简单、更安全。本文参考《Java编程思想》总结了使用泛型过程中需要注意的问题,并为大家提供了一些泛型相关的面试题供大家学习和使用。泛型相关问题1.泛型类型引用传递问题在Java中,不允许如下形式的引用传递:ArrayListarrayList1=newArrayList();//编译错误ArrayListarrayList1=newArrayList();//编译错误先看第一种情况,将第一种情况展开为如下形式:ArrayListarrayList1=newArrayList();arrayList1.add(newObject());arrayList1。add(newObject());ArrayListarrayList2=arrayList1;//编译错误其实在第4行代码会有编译错误。好吧,让我们假设它编译正确。那么当我们使用arrayList2引用的get()方法获取值时,返回的都是String类型的对象,但是实际上里面已经存放了Object类型的对象,所以会出现ClassCastException。所以为了避免这种极易出错的情况,Java不允许这样的引用传递。(这也是泛型出现的原因,就是为了解决类型转换的问题,我们不能违背它的初衷)。再看第二种情况,将第二种情况展开为如下形式:ArrayListarrayList1=newArrayList();arrayList1.add(newString());arrayList1.add(newString());ArrayListarrayList2=arrayList1;//编译错误是正确的,这种情况比第一种情况好多了,至少,我们在使用arrayList2取值的时候,不会出现ClassCastException,因为是String转Object的。然而,这样做的意义何在?之所以出现泛型,就是为了解决类型转换的问题。我们使用了泛型,但最终还是要逼自己,这违背了泛型设计的初衷。所以java不允许这样。再说了,如果你用arrayList2往里面add()一个新的对象,那么到时候获取的时候,我怎么知道我取出来的对象是String类型还是Object类型呢?因此,要特别注意泛型中的引用传递问题。2.泛型变量不能是基本数据类型。例如,没有ArrayList,只有ArrayList。因为擦除类型时,ArrayList原类中的类型变量(T)被Object替换,但Object类型不能存储double值。3、运行时类型查询ArrayListarrayList=newArrayList();if(arrayListinstanceofArrayLi因为类型擦除后,ArrayList中只剩下原来的类型,泛型信息String不存在了。4.静态方法和静态类中泛型的问题泛型类中的静态方法和静态变量不能使用泛型类声明的泛型类型参数publicclassTest2{publicstaticTone;//编译错误publicstaticTshow(Tone){//编译errorreturnnull;}}因为泛型类中泛型参数的实例化是在定义泛型类型对象(如ArrayList)时指定的,静态变量和静态方法不需要使用对象来调用。对象没有创建,怎么判断这个泛型参数的类型,所以当然是错误的。但是要注意区分以下情况之一:publicclassTest2{publicstaticTshow(Tone){//这个是正确的returnnull;}}因为这是一个泛型方法,泛型方法中使用的T是T在方法本身中定义,而不是泛型类中的T。泛型相关面试题1.Java中的泛型是什么?使用泛型有什么好处?泛型是一种参数化类型的机制。它可以让代码适合各种类型,从而写出更通用的代码,比如集合框架。泛型是一种编译时类型确认机制。它提供编译时类型安全,确保只有正确类型的对象才能用于泛型类型(通常是泛型集合),并在运行时避免ClassCastException。2.Java的泛型是如何工作的?什么是类型擦除?泛型的正常工作是依靠编译器在编译源代码时进行类型检查,然后在出现类型参数的地方进行类型擦除和插入强制转换的相关指令来实现。编译器在编译时擦除所有与类型相关的信息,因此在运行时不存在与类型相关的信息。例如,List在运行时仅由List类型表示。为什么要抹掉它?这是为了避免类型膨胀。3.什么是泛型中的限定通配符和非限定通配符?合格的通配符限制类型。合格的通配符有两种,一种是,通过保证类型必须是T的子类来设置类型的上界,另一个是,保证类型必须是T类的父类才能设置类型的下界。泛型类型必须使用范围内的类型进行初始化,否则会导致编译错误。另一方面,代表一个不合格的通配符,因为可以被任何类型代替。4.List<有什么区别?扩展T>和List?这与之前的面试题有关。有时候面试官会用这个问题来评估你对泛型的理解程度,而不是直接问Whatdoyoumeanbyqualifiedwildcardsandunqualifiedwildcards。这两个List声明是有限通配符的示例。列表可以接受任何继承自T的类型列表,而List可以接受由T的父类组成的任何List。例如List可以接受List或List。可以在本段中出现的链接中找到更多信息。5.如何编写接受泛型参数并返回泛型类型的泛型方法?编写泛型方法并不难。您需要将原始类型替换为泛型类型,例如使用T、E或K、V等广泛认可的类型占位符。有关泛型方法的示例,请参阅Java集合框架。在最简单的情况下,泛型方法可能如下所示:publicVput(Kkey,Vvalue){returncache.put(key,value);}6.如何在Java中使用泛型编写带参数的类?这是上面一道面试题的延伸。面试官可能会要求你用泛型写一个类型安全的类,而不是写一个泛型方法。关键仍然是使用泛型而不是原始类型,并使用JDK中采用的标准占位符。7、写一个通用程序实现LRU缓存?这是一个适合任何喜欢Java编程的人的练习。给你一个提示,LinkedHashMap可以用来实现一个固定大小的LRU缓存。当LRU缓存已满时,它将从缓存中删除最旧的键值对。LinkedHashMap提供了一个名为removeEldestEntry()的方法,put()和putAll()会调用该方法来删除最旧的键值对。8.可以将List传递给接受List的方法吗?(见上面的注释)对于不熟悉泛型的人来说,这个Java泛型主题可能看起来很混乱,因为乍一看String是一种Object,所以当需要List时应该使用List,但是这个事实并非如此。这样做会导致编译错误。如果你进一步思考,你会发现Java这样做是有道理的,因为List可以存储任何类型的对象,包括String、Integer等,而List只能用来存储字符串。9.Array中可以使用泛型吗?这可能是Java泛型面试题中最简单的一道了,当然前提是你要知道Array其实并不支持泛型,这也是为什么JoshuaBloch在EffectiveJava中写到推荐使用List而不是Array,因为List可以提供编译时类型安全保证,但Array不能。10.如何防止Java中的未检查类型警告?如果混合使用泛型类型和原始类型,比如下面的代码,Java5的javac编译器会产生未检查类型警告,比如ListrawList=newArrayList()注意:Hello.java使用未检查或不安全的操作;可以使用@SuppressWarnings("unchecked")注释来阻止这种警告。11、List和Java中原始类型List有什么区别?原始类型和参数类型的主要区别在于,编译器在编译时不会对原始类型进行类型安全检查,而是会对参数进行类型检查,并通过使用Object作为类型,您告诉编译器该方法可以接受任何类型的对象,例如String或Integer。本题的重点是对泛型中原始类型的正确理解。它们之间的第二个区别是,您可以将任何带参数的泛型类型传递给接受原始类型List的方法,但不能将List传递给接受List的方法,因为它会产生Compile错误。12.Java中的List和List有什么区别?这个问题看起来和上一个问题很像,但本质上是完全不同的。List是未知类型的List,而List实际上是任意类型的List。您可以将List、List分配给List,但不能将List分配给List。ListlistOfAnyType;ListlistOfObject=newArrayList();ListlistOfString=newArrayList();ListlistOfInteger=newArrayList();listOfAnyType=listOfString;//legallistOfAnyType=listOfInteger;//legallistOfObjectType=(List)listOfString;//compilererror-in-convertible13、List与原始类型List的区别。这道题类似于“原始类型和带参数的类型有什么区别”。参数化类型是类型安全的,其类型安全由编译器保证,但原始类型List不是类型安全的。您不能将String以外的任何类型的Object存储到String类型的List中,但您可以将任何类型的对象存储到原始List中。对于通用参数化类型,您不需要强制转换,但对于基本类型,您需要显式强制转换。ListlistOfRawTypes=newArrayList();listOfRawTypes.add("abc");listOfRawTypes.add(123);//编译器允许这样做——但在运行时会有异常Stringitem=(String)listOfRawTypes.get(0);//RequiredExplicittypeconversionitem=(String)listOfRawTypes.get(1);//抛出ClassCastException,因为Integer无法转换为StringListlistOfString=newArrayList();listOfString.add("abcd");listOfString.add(1234);//编译错误,比运行时抛出异常要好item=listOfString.get(0);//无需显式类型转换——编译器自动转换wildcard通配符上界例程usepublicclassTest{publicstaticvoidprintIntValue(Listlist){for(Numbernumber:list){System.out.print(number.intValue()+"");}System.out.println();}publicstaticvoidmain(String[]args){List<整数>integerList=newArrayList();integerList.add(2);integerList.add(2);printIntValue(integerList);ListfloatList=newArrayList();floatList.add((float)3.3);floatList.add((float)0.3);printIntValue(floatList);}}输出:2230非法使用publicclassTest{publicstaticvoidfillNumberList(Listlist){list.add(newInteger(0));//编译错误list.add(newFloat(1.0));//编译错误}publicstaticvoidmain(String[]args){Listlist=newArrayList();list.add(newInteger(1));//编译错误list.add(newFloat(1.0));//编译错误}}List可以表示List或List,为什么不像这样添加Integer或Float呢?首先,我们知道List只能添加Integer,下面的代码是可行的:Listlist1=newArrayList();列表list2=newArrayList();前面的例子没有编译错误。如果我们将list1或list2传递给fillNumberList方法,显然会出现类型不匹配,假设不成立。因此,我们得出结论,您不能将任何对象添加到List,null除外。那么为什么可以遍历List<呢?extendsT>,因为子类必须和父类有相同的接口,这正是我们所期望的。一般使用通配符下界?superNumber>list=newArrayList();list.add(newInteger(1));list.add(newFloat(1.1));}}可以添加Number的任何子类,为什么?列表可以表示List,其中T是Number的父类,(虽然Number没有父类)。如果T是Number的父类,我们认为在List中增加Number的子类是肯定可以的。非法使用迭代List是不允许的。为什么?你知道用哪个接口来迭代一个List吗?只有使用Object类的接口才能保证集合中的所有元素都有这个接口,显然意义不大。简述其应用场景。无界通配符知道通配符的上下界其实就相当于知道了无界通配符,没有任何修饰,就一个“?”。如List,"?"可以表示任何类型,“any”是未知类型。List不等同于List,List是List的子类。此外,您不能将任何对象添加到List列表,null除外。常规使用1、当方法使用原始Object类型作为参数时,如下:publicstaticvoidprintList(Listlist){for(Objectelem:list)System.out.println(elem+"");System.out。println();}您可以选择将其更改为以下实现:publicstaticvoidprintList(Listlist){for(Objectelem:list)System.out.print(elem+"");System.out.println();}这样可以兼容更多的More输出,而不仅仅是List,如下:Listli=Arrays.asList(1,2,3);Listls=Arrays.asList(“一”,“二”,“三”);printList(li);printList(ls);