如何使用 Java 泛型来避免 ClassCastException

点击上方蓝字关注我!
如何使用 Java 泛型来避免 ClassCastException
泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用。
一句话解释什么是泛型?
泛型是相关语言特性的集合,它允许类
或方法
对各种类型的对象进行操作,同时提供编译时类型安全性检查
引入泛型之前
泛型在Java集合框架中被广泛使用,我们不使用泛型,那么代码将会是这样:
List doubleList = new LinkedList();
doubleList.add(3.5D);
Double d = (Double) doubleList.iterator().next(); //类型强制转换
doubleList中存储一个Double类型的值, 但是List并不能阻止我们往里面再添加一个String类型
比如:doubleList.add (“ Hello world ”);
最后一行的(Double)强制转换操作符将导致在遇到非 Double 对象时抛出 ClassCastException
引入泛型之后
因为直到运行时才检测到类型安全性的缺失,所以开发人员可能不会意识到这个问题,将其留给客户机(而不是编译器)来发现。泛型允许开发人员将List标记为只包含 Double 对象,从而帮助编译器提醒开发人员在列表中存储非 Double 类型的对象的问题,在编译和开发期间,就把问题解决掉
我们可以这样改造上面的代码:
List doubleList = new LinkedList();
doubleList.add(3.5D);
Double d = doubleList.iterator().next();
这时 我们再添加String类型的参数 会提示需要的类型不符合需求.

深入探索泛型类
泛型的概念
泛型是通过类型参数
引入一组类型
的类或接口.
类型参数
:是一对尖括号之间以逗号分隔的类型参数名列表。
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
泛型类型遵循语法
泛型类型遵循以下语法:
class identifier<formalTypeParameterList>{
}
interface identifier<formalTypeParameterList>{
}
interface Map<K,V> {//多个用逗号分隔
}
类型参数命名原则
Java 编程约定要求类型参数名称为单个大写字母,例如 E 表示元素,K 表示键,V 表示值,T 表示类型。避免使用像A,B,C这样没有意义的名称。
List < E > 表示一个元素列表,但是 List < B > 的意思是什么呢?
实际类型参数 替换 类型参数
泛型的类型参数
可以被替换为实际的类型参数
(类型名称)。例如,List < String > 是一个参数化类型,其中 String 是替换类型参数 E 的实际类型参数。
JAVA支持的实际类型的参数有哪些
类型参数: 类型参数 传递给 类型参数
class Container<E> {
Set elements; //E传给E
}
具体类:传递具体的类
例: List < Student > , Student
为具体类 传给E
参数化类:传递具体的参数化类
例: Set < List < Shape > >, List< Shape >
为具体的参数化类 传给E
数组类型: 传递数组
例: Map < String, String[] >, String传给K
String[]传给V
通配符: 使用问号(?)传递
例: Class < ? > , ? 传给T
声明和使用泛型
泛型的声明涉及到指定形式类型参数列表,并在整个实现过程中访问这些类型参数。使用泛型时需要在实例化泛型时将实际类型参数传递给类型参数
定义泛型的例子
在本例子中,我们实现一个简易的容器Container,该容器类型存储相应参数类型的对象,使其能够存储各种类型
class Container<E> //也可以使用实际类型的参数
{
private E[] elements;
private int index;
Container(int size)
{
elements = (E[]) new Object[size];
//本例中我们传入的是String,将Object[]转化为String[]返回
index = 0;
}
void add(E element)
{
elements[index++] = element;
}
E get(int index)
{
return elements[index];
}
int size()
{
return index;
}
}
public class GenDemo
{
public static void main(String[] args)
{
Container con = new Container(5);//使用String传给E,指定E为String类型的
con.add("North");
con.add("South");
con.add("East");
con.add("West");
for (int i = 0; i < con.size(); i++)
System.out.println(con.get(i));
}
}
指定类型参数的泛型
Container < E > 中的 E 为无界类型参数,通俗的讲就是什么类型都可以,可以将任何实际的类型参数传递给 E . 例如,可以指定 Container < Student > 、 Container < Employee > 或 Container < Person >
通过指定上限来限制传入的类
但是有时你想限制类型,比如你想 < E > 只接受 Employee 及其子类
class Employees<E extends Employee>
此时传入的E 必须为 Employee子类, new Employees< String > 是无效的.
指定多个类型限制
当然我们还可以为一个类指定多个类型 使用&分隔 :
abstract class Employee
{
private BigDecimal hourlySalary;
private String name;
Employee(String name, BigDecimal hourlySalary)
{
this.name = name;
this.hourlySalary = hourlySalary;
}
public BigDecimal getHourlySalary()
{
return hourlySalary;
}
public String getName()
{
return name;
}
public String toString()
{
return name + ": " + hourlySalary.toString();
}
}
class Accountant extends Employee implements Comparable<Accountant>
/*
Comparable < Accountant > 表明Accountant可以按照自然顺序进行比较
Comparable 接口声明为泛型类型,只有一个名为 t 的类型参数。
这个接口提供了一个 int compareTo (t o)方法,该方法将当前对象与参数(类型为 t)进行比较,
当该对象小于、等于或大于指定对象时返回负整数、零或正整数。
*/
{
Accountant(String name, BigDecimal hourlySalary)
{
super(name, hourlySalary);
}
public int compareTo(Accountant acct)
{
return getHourlySalary().compareTo(acct.getHourlySalary());
}
}
class SortedEmployees<E extends Employee & Comparable<E>>
//第一个必须为class 之后的必须为interface
{
private E[] employees;
private int index;
@SuppressWarnings("unchecked")
SortedEmployees(int size)
{
employees = (E[]) new Employee[size];
int index = 0;
}
void add(E emp)
{
employees[index++] = emp;
Arrays.sort(employees, 0, index);
}
E get(int index)
{
return employees[index];
}
int size()
{
return index;
}
}
public class GenDemo
{
public static void main(String[] args)
{
SortedEmployees se = new SortedEmployees(10);
se.add(new Accountant("John Doe", new BigDecimal("35.40")));
se.add(new Accountant("George Smith", new BigDecimal("15.20")));
se.add(new Accountant("Jane Jones", new BigDecimal("25.60")));
for (int i = 0; i < se.size(); i++)
System.out.println(se.get(i));
}
}
下界和泛型参数
假设你想要打印出一个对象列表
class Scratch_12{
public static void main(String[] args) {
{
List directions = new ArrayList();
directions.add("north");
directions.add("south");
directions.add("east");
directions.add("west");
printList(directions);
List grades = new ArrayList();
grades.add(new Integer(98));
grades.add(new Integer(63));
grades.add(new Integer(87));
printList(grades);
}
}
static void printList(List