EffectiveJava——请不要在代码中使用原生态类型

  先看一个栗子,看看能不能找出来里面的错误:

/**
 * 请不要在新代码中使用原生态类型
 * @author weishiyao
 *
 */
public class Test {

	public static void main(String[] args) {
		List<String> strings = new ArrayList<>();
		unsafeAdd(strings, new Integer(42));
		String string = strings.get(0);
		System.out.println(string);
	}

	private static void unsafeAdd(List list, Object object) {
		list.add(object);
	}
}

  运行一下,看一下报错结果:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
	at com.weishiyao.effectiveJava.entry23.Test.main(Test.java:16)

  对的,是一个ClassCastException,这是一个编译器声称的转换,因此一般会保证成功,但是我嘛在这个栗子中忽略了一条编译器的警告,就会为此而付出代价。

  但是,如果用unsafeAdd(List<Object> list, Object object)代替原生的List类型,我们的IDE会直接报错。

  在不确定或者不在乎集合中元素类型的情况下,你也许会使用原生态的类型。例如,假设想要编写一个方法,它有两个集合(set),并从中返回他们公有的元素的数量。如果不熟悉范型的话,可以参考以下方式来编写这种方法:

	static int numElementsInCommen(Set s1, Set s2) {
		int result = 0;
		for (Object object : s1) {
			if (s2.contains(object)) {
				result++;
			}
		}
		return result;
	}

  这个方法倒是可行,但是它使用了原生态类型,这是很危险的。从java1.5发行版本开始,java就提供了一种安全的替代方法,称作无限制的通配符类型,如果要使用范型,但是确定或者不关心实际的参数类型,就可以用一个问号代替。例如范型Set<E>的无限制通配符类型为Set<?>。这是最普通的参数化Set类型,可以持有任何集合。

  在无限制通配类型Set<?>和原生态类型Set之间有什么区别呢?这个问号真正起到作用了么?这一点不需要赘述,但通配符类型是安全的,原生态类型则不安全。可以将任何元素放入使用原生态类型的集合中,因此很容易破坏该集合类型的约束条件;但不能将任何元素(除了null)放到Collection<?>中。

  不要在新代码中使用原生态类型,这条规则有两个小小的例外,两者都源于“范型信息可以在运行时被擦除”这一事实。在类文字中必须使用原生形态。规范不允许使用参数化类型。换句话说,List.class,String[].class和int.class都是合法的。但是List<String>.class和List<?>则不合法。

  这条规则第二个例外与instanceof操作符有关。由于范型信息可以在运行时被擦除,一次在参数化类型而非无限制通配符上使用instanceof操作符时非法的。用无限制通配符类型代替原生态类型,对instanceof操作符的行为不回产生任何影响。在这种情况下,尖括号<>和问号?就显得多余了。下面是利用范型来使用instanceof操作符的首选方法:

				if (object instanceof Set) {	// Raw type
					Set<?> m = (Set<?>) object;	// Wildcard type
				}

  注意,一旦确定这个object是个Set,就必须将他转换成通配符类型Set<?>,而不是原生类型,这个是受检的,否则的话我们的IDE还是会报错的。

时间: 2024-08-08 01:12:00

EffectiveJava——请不要在代码中使用原生态类型的相关文章

代码中判断网络类型的类别

Reachability 中定义了3种网络状态: typedef enum { NotReachable = 0,  //无连接 ReachableViaCarrierDataNetwork, //使用3G/GPRS网络 ReachableViaWiFiNetwork  //使用WiFi网络 } NetworkStatus; 比如检测某一特定站点的接续状况,可以使用下面的代码: Reachability *r = [Reachability reachabilityWithHostName:@“

后台代码中绑定枚举类型显示为英文,要显示中文的

显示英文的情况: 是点击同页面中另外一个ddl控件显示隐藏部分,其中隐藏部分中有另一个ddl,此ddl显示为英文 this.ddl.Items.Add(EnumUtils.GetListItem(PeriodType.Month));....... 这里在页面显示中为Month 修改为:(此种格式显示中文) this.ddl.Items.Add(new ListItem(GetString(PeriodType.Month.ToString()),((int)PeriodType.Month).

Dynamics AX 2012 R2 从代码中调用SSRS Report

    平时,我们制作SSRS Report的方法主要有两种:使用Query或RDP.如果需要为报表传递参数,就要在代码中为报表参数赋值,然后在代码中调用报表.下面我总结下这两种报表在代码中传参和调用的方式: 1.使用Query作为报表数据源 1.1.Dynamic Filters属性     在VS中,需要注意Report DataSource的Dynamic Filters属性. 1.1.1.如果Dynamic Filters属性为True的话,会在Report Parameter中生成一个

「Flink」Flink中的时间类型

Flink中的时间类型和窗口是非常重要概念,是学习Flink必须要掌握的两个知识点. Flink中的时间类型 时间类型介绍 Flink流式处理中支持不同类型的时间.分为以下几种: 处理时间 Flink程序执行对应操作的系统时间.所有基于时间的操作(例如:时间窗口)都将使用运行相应operator的系统时间.例如:每个小时的处理时间窗口包括在系统时间范围内所有operator接收到的记录.例如:如果应用程序在09:15开始运行,则第一个滚动时间窗口将包括:09:15 – 10:00 之间的处理事件

该对象尚未初始化。请确保在所有其他初始化代码后面的应用程序启动代码中调用

WebAPI使用属性路由,配置config.MapHttpAttributeRoutes();后出现错误: System.InvalidOperationException: 该对象尚未初始化.请确保在所有其他初始化代码后面的应用程序启动代码中调用 HttpConfiguration.EnsureInitialized(). 在 System.Web.Http.Routing.RouteCollectionRoute.get_SubRoutes() 在 System.Web.Http.Routi

轻易解决VMware 虚拟机中被提示“请不要在虚拟机中运行此程序“

决 VMware 虚拟机 中被提示 "请不要在虚拟机中运行此程序"或者"Themida Sorry, this application cannot run under a Virtual Machine"方法 自己发现问题,解决问题后,觉得应该分享给大家.用记事本打开 VMX 文件 类似 Windows XP Professional.vmx ,在文本末尾加入一行 monitor_control.restrict_backdoor = TRUE 保存文件 现在启动

实验--使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用(杨光)

使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 攥写人:杨光  学号:20135233 ( *原创作品转载请注明出处*) ( 学习课程:<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验要求: 选择一个系统调用(13号系统调用time除外),系统调用列表参见http://codelab.shiyanlou.com/xref/linux-3.18.6/arch/x86/syscalls/sys

关于C++代码中的#pragma预处理指令

预处理指令是指在编译器编译代码时,提供按条件跳过源文件中的代码段(节).报告错误(错误信息以及行号)和警告条件,以及描绘源代码的不同区域的能力. 总是占用源代码中的单独一行,并且总是以 # 字符和预处理指令名称开头.# 字符的前面以及 # 字符与指令名称之间可以出现空白符. 下面是可用的预处理指令: #define 和 #undef,分别用于定义和取消定义条件编译符号. #if.#elif.#else 和 #endif,用于按条件跳过源代码中的节. #line,用于控制行号(在发布错误和警告信息

android在代码中四种设置控件背景颜色的方法(包括RGB)

转载请注明出处: http://blog.csdn.net/fth826595345/article/details/9208771  TextView tText=(TextView) findViewById(R.id.textv_name); //第1种: tText.setTextColor(android.graphics.Color.RED);//系统自带的颜色类 // 第2种: tText.setTextColor(0xffff00ff);//0xffff00ff是int类型的数据