• 8.1 泛型入门



    Java集合被设计成能保存任何类型的对象,具有很好的通用性。但是这样设计带来了两个问题:
    ★集合对元素没有任何限制,可能引发一些问题:想创建一个保存Dog对象的集合,但程序可以将Cat对象轻易地添加进去。
    ★由于把"对象"丢进集合时,集合失去了对象的状态信息,集合只知道它盛装的时Object,因此取出集合元素时后通常需要强制转换。
    下面介绍编译时不检查类型可能引发的异常,以及如何做到在编译时进行类型检查。

    一、编译时不检查类型的异常

    import java.util.*;
    public class ListError 
    {
    	public static void main(String[] args) 
    	{
    		//创建一个只想保存字符串的List集合
    		var strList=new ArrayList();
    		strList.add("疯狂Java讲义");
    		strList.add("疯狂Android讲义");
    		//一不小心加入一个Integer对象
    		strList.add(5);
    		strList.forEach(str->System.out.println(((String) str).length()));
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    8
    11
    Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
    	at ListError.lambda$main$0(ListError.java:12)
    	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    	at ListError.main(ListError.java:12)
    
    输出完成 (耗时 0 秒) - 正常终止
    

    二、使用泛型

    Java 5以后,Java引入了"参数化类型(parameterized type)"的概念,允许程序在创建集合时指定元素的类型。Java的参数化类型被称为泛型(Generic)。

    import java.util.*;
    public class GenericList 
    {
    	public static void main(String[] args) 
    	{
    		//创建一个只想保存字符串的集合
    		List<String> strList=new ArrayList<String>();
    		strList.add("疯狂Java讲义");
    		strList.add("疯狂Android讲义");
    		//下面代码将引起编译错误
    		strList.add(5);//GenericList.java:11: 错误: 不兼容的类型: int无法转换为String
    		strList.forEach(str->System.out.println(((String) str).length()));
    	}
    }
    

    上面创建了一个只能保存字符串的List集合。创建该种集合的方法:在集合接口、类后,即表明这个集合接口、集合类只能保存特定类型的对象。

    三、Java 9增强的"菱形"语法

    在Java 7 以前使用带泛型的接口、类定义变量,那么调用构造器创建对象时构造器的后面也必须带有泛型。例如:

    List<String> strList=new ArrayList<String>();
    Map<String,Integer> scores=new HashMap<String,Integer>();
    

    从Java 7开始,Java允许在构造器后不需要带完整的泛型信息,只要给出一对尖括号(<>)即可,Java可以推断出尖括号里是什么泛型信息。所以上面两条语句可以改成:

    List<String> strList=new ArrayList<>();
    Map<String,Integer> scores=new HashMap<>();
    

    把两个尖括号放在一起非常想菱形,这种语法被称为"菱形"语法
    下面示范Java 7以后版本的菱形语法:

    import java.util.List;
    import java.util.ArrayList;
    import java.util.*;
    public class DiamondTest 
    {
    	public static void main(String[] args) 
    	{
    		//java自动推断出ArrayList的<>里应该是String
    		List<String> books=new ArrayList<>();
    		books.add("疯狂Java讲义");
    		books.add("疯狂Android讲义");
    		//遍历books集合,集合元素就是String类型
    		books.forEach(ele->System.out.println(ele+"————>"+ele.length()));
    
    		//自动推断出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类型,value是List<String>类型
    		schoolsInfo.forEach((key,value)->System.out.println(key+"-->"+value));
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    疯狂Java讲义————>8
    疯狂Android讲义————>11
    孙悟空-->[三星洞, 水帘洞]
    
    输出完成 (耗时 0 秒) - 正常终止
    

    从上面的程序可以看出,"菱形"语法对原有的泛型并没有改变,只是更好地简化泛型编程。但需要var声明变量时,编译器无法推断泛型类型。因此,使用var声明变量时,程序无法使用"菱形"语法。
    Java 9再次增强了"菱形"语法,它甚至允许在创建匿名内部类时使用菱形语法,Java根据上下文来推断匿名内部类的泛型类型。下面展示在匿名内部类中使用菱形语法:

    
    interface Foo<T>
    {
    	void test(T t);
    }
    public class AnnoymousDiamond
    {
    	public static void main(String[] args)
    	{
    		// 指定Foo类中泛型为String
    		Foo<String> f = new Foo<>()
    		{
    			// test()方法的参数类型为String
    			public void test(String t)
    			{
    				System.out.println("test方法的t参数为:" + t);
    			}
    		};
    		// 使用泛型通配符,此时相当于通配符的上限为Object
    		Foo<?> fo = new Foo<>()
    		{
    			// test()方法的参数类型为Object
    			public void test(Object t)
    			{
    				System.out.println("test方法的Object参数为:" + t);
    			}
    		};
    		// 使用泛型通配符,通配符的上限为Number
    		Foo<? extends Number> fn = new Foo<>()
    		{
    			// 此时test()方法的参数类型为Number
    			public void test(Number t)
    			{
    				System.out.println("test方法的Number参数为:" + t);
    			}
    		};
    	}
    }
    

    上面程序先定义了一个带泛型的接口,接下来分别示范了三种在匿名内部类中使用菱形语法情形。第一次声明变量时明确指定泛型为String类型,因此匿名内部类中的T代表String类型。第二次声明变量时使用通配符来代表泛型(相当于通配符的上限为Object),系统只能推断出T代表Object;第三次声明变量时使用了带上限(上限是Number)的通配符,因此可以推断出T代表Number类。

  • 相关阅读:
    【java并发编程】ReentrantLock 可重入读写锁
    浏览器上写代码,4核8G微软服务器免费用,Codespaces真香
    【低码】asp.net core 实体类可生产 CRUD 后台管理界面
    深度学习和图形学渲染的结合和应用 Wonder
    Sentinel Dashboard 规则 持久化到Nacos
    一条更新SQL的内部执行及日志模块
    一条Sql的执行过程
    (数据科学学习手札136)Python中基于joblib实现极简并行计算加速
    老板给我安排了个测试环境治理的活
    C# 图片沿中心点进行角度旋转
  • 原文地址:https://www.cnblogs.com/weststar/p/12589939.html
Copyright © 2020-2023  润新知