持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情
1 泛型入门
Java集合有个缺点——当我们把一个对象“丢进”集合里后,集合就会“忘记”这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了Object类型(其运行时类型没变)。
Java集合之所以被设计成这样,是因为设计集合的程序员不会知道我们用它来保存什么类型的对象,所以他们把集合设计成能保存任何类型的对象,只要求具有很好的通用性。但这样做带来如下两个问题:
- 集合对元素类型没有任何限制,这样可能引发一些问题。例如,想创建一个只能保存Dog对象的集合,但程序也可以轻易地将Cat对象“丢”进去,所以可能引发异常。
- 由于把对象“丢进”集合时,集合丢失了对象的状态信息,集合只知道它盛装的是Object,因此取出集合元素后通常还需要进行强制类型转换。这种强制类型转换既增加了编程的复杂度,也可能引发ClassCastException异常。
1.1 编译时不检查类型的异常
public class ListErr
{
public static void main(String[] args)
{
// 创建一个只想保存字符串的List集合
List strList=new ArrayList();
strList.add("疯狂Java讲义");
strList.add("疯狂Ajax讲义");
strList.add("轻量级Java EE企业应用实战");
// “不小心”把一个Integer对象“丢进”了集合
strList.add(5); //①
for (int i=0; i < strList.size() ; i++ )
{
// 因为List里取出的全部是Object,所以必须进行强制类型转换
// 最后一个元素将出现ClassCastException异常
String str=(String)strList.get(i); //②
}
}
}
1.2 手动实现编译时检查类型
如果希望创建一个List对象,且该List对象中只能保存字符串类型,那么我们可以扩展ArrayList类。下面程序创建了一个StrList集合类,该集合里只能存放String对象。
// 自定义一个StrList集合类,使用组合的方式来复用ArrayList类
class StrList
{
private List strList=new ArrayList();
// 定义StrList的add方法
public boolean add(String ele)
{
return strList.add(ele);
}
// 重写get()方法,将get()方法的返回值类型改为String类型
public String get(int index)
{
return (String)strList.get(index);
}
public int size()
{
return strList.size();
}
}
public class CheckType
{
public static void main(String[] args)
{
// 创建一个只想保存字符串的List集合
StrList strList=new StrList();
strList.add("疯狂Java讲义");
strList.add("疯狂Android讲义");
strList.add("轻量级Java EE企业应用实战");
// 下面语句不能把Integer对象“丢进”集合中,否则将引起编译错误
strList.add(5); //①
System.out.println(strList);
for (int i=0; i < strList.size() ; i++ )
{
// 因为StrList里元素的类型就是String类型
//所以无须进行强制类型转换
String str=strList.get(i);
}
}
}
这种做法虽然有效,但局限性非常明显——程序员需要定义大量的List子类,这是一件让人沮丧的事情。从Java 5以后,Java引入了“参数化类型(parameterized type)”的概念,允许我们在创建集合时指定集合元素的类型。Java的参数化类型被称为泛型(Generic)。
1.3 使用泛型
对于前面的ListErr.java程序,可以使用泛型改进这个程序。
public class GenericList
{
public static void main(String[] args)
{
// 创建一个只想保存字符串的List集合
List<String> strList=new ArrayList<String>(); //①
strList.add("疯狂Java讲义");
strList.add("疯狂Android讲义");
strList.add("轻量级Java EE企业应用实战");
// 下面代码将引起编译错误
strList.add(5); //②
for (int i=0; i < strList.size() ; i++ )
{
// 下面代码无须进行强制类型转换
String str=strList.get(i); //③
}
}
}
1.4 Java 7泛型的“菱形”语法
从Java 7开始,Java允许在构造器后不需要带完整的泛型信息,只要给出一对尖括号(<>)即可,Java可以推断尖括号里应该是什么泛型信息。即上面两条语句可以改写
List<String> strList=new ArrayList<>();
Map<String , Integer> scores=new HashMap<>();
public class DiamondTest
{
public static void main(String[] args)
{
// Java自动推断出ArrayList的<>里应该是String
List<String> books=new ArrayList<>();
books.add("疯狂Java讲义");
books.add("疯狂Android讲义");
books.add("轻量级Java EE企业应用实战");
// 遍历时集合元素就是String
for (String book : books )
{
System.out.println(book);
}
//Java自动推断出HashMap的<>里应该是String, List<String>
Map<String , List<String>> schoolsInfo=new HashMap<>();
// Java自动推断出ArrayList的<>里应该是String
List<String> schools=new ArrayList<>();
schools.add("斜月三星洞");
schools.add("西天取经路");
schoolsInfo.put("孙悟空" , schools);
// 遍历Map时,Map的key是String类型
for (String key : schoolsInfo.keySet())
{
// value是List<String>类型
List<String> list=schoolsInfo.get(key);
System.out.println(key + "-->" + list);
}
}
}