泛型入门

41 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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);
                  }
                }
            }