Java泛型是Java SE 5中引入的一项改进机制,它是Java语言中的一种特殊类型,主要用于对类和方法进行泛化处理。Java泛型可以对任意类型进行泛化处理,从而使得程序的通用性更强,代码实现更加简洁优雅。本文将从多个方面对Java泛型做详细的阐述。
一、JAVA中泛型的经典解释
泛型就是参数化类型。未使用泛型时,我们通常使用Object对象来表示任何类型的数据,例如:
public static void main(String[] args) { List list = new ArrayList(); //未使用泛型 list.add("hello"); String str = (String) list.get(0); //需要强制转换 }
在上面的例子中,我们需要将Object类型转换为String类型,这样不仅繁琐,而且容易出现类型转换异常。Java泛型的出现可以很好地解决这个问题:
public static void main(String[] args) { List<String> list = new ArrayList<>(); //使用泛型 list.add("hello"); String str = list.get(0); //不需要强制转换 }
使用泛型后,我们可以直接获得String类型的值,不需要进行类型转换,代码更加简洁、安全可靠。
二、泛型的类型参数
Java泛型的核心概念就是泛型类型参数,即在定义类或方法时所使用的类型表示符。在Java中,泛型类型参数使用尖括号(<...>)来表示,例如:
public class MyList<E> { private List<E> list = new ArrayList<>(); public void add(E e) { list.add(e); } public E get(int index) { return list.get(index); } }
在上面的例子中,我们定义了一个包含泛型类型参数E的类MyList,然后使用List<E>来定义类的成员变量,从而实现对泛型类型E的使用。
泛型类型参数可以用于方法中的参数、返回值和局部变量,例如:
public <T> T getValue(T t) { return t; }
在上面的例子中,我们定义了一个泛型方法getValue,使用泛型类型参数T来表示方法的参数和返回值类型。例如,调用getValue("hello")方法将返回字符串"hello"。
三、通配符?的使用
通配符?是Java泛型中一个比较重要的概念,它可以代表任何类型的实例,例如:
public static double sum(List<? extends Number> list) { double result = 0; for (Number num : list) { result += num.doubleValue(); } return result; }
在上面的例子中,我们定义了一个静态方法sum,该方法接收一个List<? extends Number>类型的参数,并遍历该列表中的所有元素,累加它们的double值,并返回累加结果。通配符?的作用在于可以适配任何类型的列表,比如List<Double>、List<Integer>等。
通配符?还可以用于上下界限定,例如:
List<? extends Number> list = new ArrayList<>(); list.add(1); //编译报错
在上面的例子中,我们定义了一个List<? extends Number>类型的列表,该列表不能添加任何元素,因为这违反了上界限定,而我们无法确定该列表具体是什么类型。
四、常见泛型面试题
1. 什么是Java泛型?
Java泛型是一种特殊的类型,可以对类和方法进行泛化处理,从而让程序的通用性更强,代码实现更加简洁优雅。
2. 泛型与多态的区别是什么?
泛型是Java SE 5中引入的一项改进机制,是一种编译时概念,而多态是Java的基本特性,是一种运行时概念。泛型是对类型进行泛化处理,使得同一份代码可以适应不同类型的数据,而多态是通过父类或接口来引用子类或实现类的对象,从而实现代码的重用和灵活性。
3. Java泛型中的上下界限定有哪些?
Java泛型中的上下界限定主要有三种:
- <? extends T>:表示通配符的上界限定,代表T或T的子类。
- <? super T>:表示通配符的下界限定,代表T或T的父类。
- <T extends Comparable<T>>:表示泛型类型参数T需要实现Comparable接口。
4. 什么是泛型擦除?
泛型擦除是Java泛型中的一种特殊机制,它是在编译时实现的。泛型擦除的作用在于使得泛型类型参数在运行时被擦除,从而避免了在运行时对类型进行判断的开销。例如,在编译后,List<String>和List<Integer>都变成了List<Object>。
5. 如何解决泛型类型擦除所带来的问题?
为了解决泛型类型擦除所带来的问题,Java提供了一些解决方案,例如:
- 使用泛型通配符来限制类型。
- 使用反射获取泛型类型参数。
- 使用泛型辅助库,例如Google的Guava库。
五、代码示例
下面是一个关于Java泛型的完整示例:
public class Main { public static void main(String[] args) { List<String> stringList = new ArrayList<>(); stringList.add("hello"); stringList.add("world"); System.out.println(join(stringList, ", ")); //输出"hello, world" List<Integer> integerList = new ArrayList<>(); integerList.add(1); integerList.add(2); System.out.println(join(integerList, " + ")); //输出"1 + 2 = 3" List<Double> doubleList = new ArrayList<>(); doubleList.add(1.2); doubleList.add(3.4); System.out.println(join(doubleList, " + ")); //输出"1.2 + 3.4 = 4.6" } public static <E> String join(List<E> list, String separator) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < list.size(); i++) { if (i > 0) { sb.append(separator); } sb.append(String.valueOf(list.get(i))); } if (list.size() > 1) { sb.append(" = "); } if (list.size() > 0) { sb.append(String.valueOf(sum(list))); } return sb.toString(); } public static <E extends Number> double sum(List<E> list) { double result = 0; for (Number num : list) { result += num.doubleValue(); } return result; } }