一、泛型之前
public class BeforeGeneric { static class ArrayList{
//泛型之前的通用程序设计 private Object[] elements=new Object[0]; public Object get(int i){ return elements[i]; } public void add(Object o){ //这里的实现,只是为了演示,不具有任何参考价值 int length=elements.length; Object[] newElments=new Object[length+1]; for(int i=0;i
public
static
void
main(String[] args) { ArrayList stringValues=
new ArrayList(); stringValues.add(
1);
//可以向数组中添加任何类型的对象
//问题1——获取值时必须强制转换 String str=(String) stringValues.
get(
0);
//问题2——上述强制转型编译时不会出错,而运行时报异常java.lang.ClassCastException } }
二、泛型
针对利用继承来实现通用程序设计所产生的问题,泛型提供了更好的解决方案:类型参数。例如,ArrayList类用一个类型参数来指出元素的类型。
ArrayList<String> stringValues=new ArrayList<String>();
这样的代码具有更好的可读性,我们一看就知道该集合用来保存String类型的对象,而不是仅仅依赖变量名称来暗示我们期望的类型。
public class GenericType { public static void main(String[] args) { ArrayList
stringValues=
new ArrayList
(); stringValues.add(
"str"); stringValues.add(
1);
//编译错误 } }
三、Java泛型的实现原理
擦除
public class GenericType { public static void main(String[] args) { ArrayList
arrayString=
new ArrayList
(); ArrayList
arrayInteger=
new ArrayList
(); System.
out.println(arrayString.getClass()==arrayInteger.getClass()); } }
原始类型就是泛型类型擦除了泛型信息后,在字节码中真正的类型。无论何时定义一个泛型类型,相应的原始类型都会被自动提供。原始类型的名字就是删去类型参数后的泛型类型的类名。擦除类型变量,并替换为限定类型(T为无限定的类型变量,用Object替换)。
//泛型类型 class Pair
{
private T
value;
public T
getValue() {
return
value; }
public
void
setValue(T
value) {
this.
value =
value; } }
//原始类型 class Pair { private Object value; public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } }
因为在Pair中,T是一个无限定的类型变量,所以用Object替换。如果是Pair,擦除后,类型变量用Number类型替换。
public class ReflectInGeneric { public static void main(String[] args) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { ArrayList
array=
new ArrayList
(); array.add(
1);
//这样调用add方法只能存储整形,因为泛型类型的实例为Integer array.getClass().getMethod(
"add", Object.class).invoke(array,
"asd");
for (
int i=
0;i
out.println(array.
get(i)); } } }
public class Test {
public static void main(String[] args) { /不指定泛型的时候*/ int i=Test.add(1, 2); //这两个参数都是Integer,所以T替换为Integer类型 Number f=Test.add(1, 1.2);//这两个参数一个是Integer,另一个是Float,所以取同一父类的最小级,为Number Object o=Test.add(1, "asd");//这两个参数一个是Integer,另一个是String,所以取同一父类的最小级,为Object /指定泛型的时候*/ int a=Test.
add(
1,
2);
//指定了Integer,所以只能为Integer类型或者其子类
int b=Test.
add(
1,
2.2);
//编译错误,指定了Integer,不能为Float Number c=Test.
add(
1,
2.2);
//指定为Number,所以可以为Integer和Float }
//这是一个简单的泛型方法
public
static
T
add(T x,T y){
return y; } }
正确的运转
Pair<Integer> pair=new Pair<Integer> (); pair.setValue(3); Integer integer=pair.getValue(); System.out.println(integer);
ArrayList<String> arrayList1=new ArrayList<Object>();//编译错误 ArrayList<Object> arrayList1=new ArrayList<String>();//编译错误
我们先看第一种情况,将第一种情况拓展成下面的形式:
ArrayList<Object> arrayList1=new ArrayList<Object>(); arrayList1.add(new Object()); arrayList1.add(new Object()); ArrayList<String> arrayList2=arrayList1;//编译错误
ArrayList<String> arrayList1=new ArrayList<String>(); arrayList1.add(new String()); arrayList1.add(new String()); ArrayList<Object> arrayList2=arrayList1;//编译错误
ArrayList<String> arrayList=new ArrayList<String>();
因为类型擦除之后,ArrayList只剩下原始类型,泛型信息String不存在了。那么,运行时进行类型查询的时候使用下面的方法是错误的
if( arrayList instanceof ArrayList<String>)
java限定了这种类型查询的方式,?为通配符,也即非限定符。
if( arrayList instanceof ArrayList
>)
public class Test2
{
public
static T one;
//编译错误
public
static T
show(T one){
//编译错误
return
null; } }
public class Test2
{
public
static
T
show(T one){
//这是正确的
return
null; } }
- Java中的泛型是什么 ? 使用泛型的好处是什么?
泛型是一种参数化类型的机制。它可以使得代码适用于各种类型,从而编写更加通用的代码,例如集合框架。
泛型是一种编译时类型确认机制。它提供了编译期的类型安全,确保在泛型类型(通常为泛型集合)上只能使用正确类型的对象,避免了在运行时出现ClassCastException。
2、Java的泛型是如何工作的 ? 什么是类型擦除 ?
泛型的正常工作是依赖编译器在编译源码的时候,先进行类型检查,然后进行类型擦除并且在类型参数出现的地方插入强制转换的相关指令实现的。
编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如List在运行时仅用一个List类型来表示。为什么要进行擦除呢?这是为了避免类型膨胀。 - 什么是泛型中的限定通配符和非限定通配符 ?
限定通配符对类型进行了限制。有两种限定通配符,一种是
public V put(K key, V value) { return cache.put(key, value); }
- Java中如何使用泛型编写带有参数的类?
这是上一道面试题的延伸。面试官可能会要求你用泛型编写一个类型安全的类,而不是编写一个泛型方法。关键仍然是使用泛型类型来代替原始类型,而且要使用JDK中采用的标准占位符。 - 编写一段泛型程序来实现LRU缓存?
对于喜欢Java编程的人来说这相当于是一次练习。给你个提示,LinkedHashMap可以用来实现固定大小的LRU缓存,当LRU缓存已经满了的时候,它会把最老的键值对移出缓存。LinkedHashMap提供了一个称为removeEldestEntry()的方法,该方法会被put()和putAll()调用来删除最老的键值对。 - 你可以把List传递给一个接受List参数的方法吗?
对任何一个不太熟悉泛型的人来说,这个Java泛型题目看起来令人疑惑,因为乍看起来String是一种Object,所以List应当可以用在需要List的地方,但是事实并非如此。真这样做的话会导致编译错误。如果你再深一步考虑,你会发现Java这样做是有意义的,因为List可以存储任何类型的对象包括String, Integer等等,而List却只能用来存储Strings。
List<Object> objectList; List<String> stringList; objectList = stringList; //compilation error incompatible types
- Array中可以用泛型吗?
这可能是Java泛型面试题中最简单的一个了,当然前提是你要知道Array事实上并不支持泛型,这也是为什么Joshua Bloch在Effective Java一书中建议使用List来代替Array,因为List可以提供编译期的类型安全保证,而Array却不能。 - 如何阻止Java中的类型未检查的警告?
如果你把泛型和原始类型混合起来使用,例如下列代码,Java 5的javac编译器会产生类型未检查的警告
,例如List rawList = new ArrayList()
注意: Hello.java使用了未检查或称为不安全的操作;
这种警告可以使用@SuppressWarnings(“unchecked”)注解来屏蔽。
11、Java中List和原始类型List之间的区别?
原始类型和带参数类型之间的主要区别是,在编译时编译器不会对原始类型进行类型安全检查,却会对带参数的类型进行检查,通过使用Object作为类型,可以告知编译器该方法可以接受任何类型的对象,比如String或Integer。这道题的考察点在于对泛型中原始类型的正确理解。它们之间的第二点区别是,你可以把任何带参数的泛型类型传递给接受原始类型List的方法,但却不能把List传递给接受List的方法,因为会产生编译错误。
12、Java中List
List
listOfAnyType; List<Object> listOfObject = new ArrayList<Object>(); List<String> listOfString = new ArrayList<String>(); List<Integer> listOfInteger = new ArrayList<Integer>(); listOfAnyType = listOfString; //legal listOfAnyType = listOfInteger; //legal listOfObjectType = (List<Object>) listOfString; //compiler error - in-convertible types
List listOfRawTypes = new ArrayList(); listOfRawTypes.add("abc"); listOfRawTypes.add(123); //编译器允许这样 - 运行时却会出现异常 String item = (String) listOfRawTypes.get(0); //需要显式的类型转换 item = (String) listOfRawTypes.get(1); //抛ClassCastException,因为Integer不能被转换为String List
listOfString =
new ArrayList(); listOfString.
add(
"abcd"); listOfString.
add(
1234);
//编译错误,比在运行时抛异常要好
item = listOfString.
get(
0);
//不需要显式的类型转换 - 编译器自动转换
通配符
通配符上界
常规使用
public class Test { public static void printIntValue(List
list) { for (Number number : list) { System.out.print(number.intValue()+" "); } System.out.println(); } public static void main(String[] args) { List
integerList=
new ArrayList
(); integerList.add(
2); integerList.add(
2); printIntValue(integerList); List
floatList=
new ArrayList
(); floatList.add((
float)
3.3); floatList.add((
float)
0.3); printIntValue(floatList); } }
public class Test {
public static void fillNumberList(List
extends Number> list) { list.add(new Integer(0));//编译错误 list.add(new Float(1.0));//编译错误 } public static void main(String[] args) { List
extends Number> list=new ArrayList(); list.add(new Integer(1));//编译错误 list.add(new Float(1.0));//编译错误 } }
List
List
extends Number> list1=new ArrayList
();
List
extends Number> list2=new ArrayList
();
public class Test {
public static void fillNumberList(List
super Number> list) { list.add(new Integer(0)); list.add(new Float(1.0)); } public static void main(String[] args) { List
super Number> list=new ArrayList(); list.add(new Integer(1)); list.add(new Float(1.1)); } }
public static void printList(List
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/176935.html原文链接:https://javaforall.net
