Java范型

时间:2022-09-22 10:41:59

泛型不用考虑对象的具体类型。优点在于,因为不用考虑对象的具体类型所以可以对一类对象执行一定的相同操作;缺点在于,因为没有考虑对象的具体类型所以就不能使用对象自带的接口函数。泛型的最佳用同是实现容器类。在java中,范型是在编译器中实现的,而不是在虚拟机中实现的,虚拟机对范型一无所知。因此,编译器一定要把范型类修改为普通类,才能够在虚拟机中执行。在java中,这种技术称之为“擦除”,也就是用Object类型替换范型(Comparable来替换所有范型)。当需要用到其他约束中定义的方法的时候,通过插入强制转化代码来实现。Wildcard支持另外一个关键字super,而范型方法不支持super关键字。下面是一个简单的泛型类的代码
package demo;

public class Name<T> {

public Name() {
this.firstName = null;
this.secondName = null;
}

public Name(T firstName, T secondName) {
this.firstName = firstName;
this.secondName = secondName;
}

public T getFirstName() {
return firstName;
}

public void setFirstName(T firstName) {
this.firstName = firstName;
}

public T getSecondName() {
return secondName;
}

public void setSecondName(T secondName) {
this.secondName = secondName;
}

private T firstName;

private T secondName;

}

==================================================================
package demo;

public class Name {
public Name() {
this.firstName = null;
this.secondName = null;
}

public Name(Object firstName, Object secondName) {
this.firstName = firstName;
this.secondName = secondName;
}

public Object getFirstName() {
return firstName;
}

public void setFirstName(Object firstName) {
this.firstName = firstName;
}

public Object getSecondName() {
return secondName;
}

public void setSecondName(Object secondName) {
this.secondName = secondName;
}

private Object firstName;

private Object secondName;

}

每当你用一个具体类去实例化该范型时,编译器都会在原生类的基础上,通过强制约束和在需要的地方添加强制转换代码来满足需求,但是不会生成更多的具体的类。
Pair<Employee> buddies = new Pair<Employee>();
在上述原生代码中,此处参数类型是Object,理论上可以接纳各种类型,但编译器通过强制约束在此使用Employee(及子类)类型的参数,其他类型编译器一律报错buddies.setFirst(new Employee("张三")); 在上述原生代码中,getFirst()的返回值是一个Object类型,是不可以直接赋给类型为Employee的buddy的,但编译器在此做了手脚,添加了强制转化代码,实际代码应该是Employee buddy = (Employee)buddies.getFirst();这样就合法了。但编译器做过手脚的代码你是看不到的,他是以字节码的形式完成的。Employee buddy = buddies.getFirst();一般情况下不要涉及类型的具体信息。范型类可以继承自某一个父类,或者实现某个接口,或者同时继承父类并且实现接口。这样的话,就可以对类型调用父类或接口中定义的方法了。
public class Pair<T extends Comparable>
...{
public boolean setSecond(T newValue) ...{
boolean flag = false;
If(newValue.compareTo(first)>0) ...{
second = newValue;
flag = true;
}
return flag;
}

private T first;
private T second;
}

上面的范型T被添加了一个约束条件,那就是他必须实现Comparable接口,这样的话,就可以对范型T使用接口中定义的方法了,也就可以实现2个元素大小的比较。为了简化范型的设计,无论是继承类还是实现接口,一律使用extends关键字。若同时添加多个约束,各个约束之间用“&”分隔,比如:public class Pair<T extends Comparable & Serializable>。那么编译器是如何处理这种情况呢?前面讲过,范型类最终都会被转化为原生类。在前面没有添加约束的时候,编译器将范型通通替换为Object;而增加了约束之后,通通用第一个约束来替换范型
ArrayList<Integer> l = new ArrayList<Integer>();
test(l); //此处编译器会报错!!
Integer确实是Number的子类,但是,ArrayList<Integer>并不是ArrayList<Number>的子类,二者之间没有任何的继承关系
public static void test(ArrayList<Number> l) ...{
l.add(new Float(2));
}

在函数内部,我们把Float类型的元素插入到链表中。因为链表是Number类型,这条语句没问题。但是,如果实参是一个Integer类型的链表,他能存储Float类型的数据吗??显然不能,这样就会造成运行时错误。于是,编译器干脆就不允许进行这样的传递。在向容器类添加内容的时候可能造成类型不匹配。
===================================================================

// 1.在定义方法的时候使用Wildcard(也就是下述代码中的问号)。
public static void test1(ArrayList<? extends Number> l) ...{
Integer n = new Integer(45);
Number x = l.get(0); //从链表中取数据是允许的
l.add(n); //错误!!往链表里面插入数据是被编译器严格禁止的!!
}

// 2.定义一个范型方法。代码如下:
public static <T extends Number> void test2(ArrayList<T> l) ...{
Number n = l.get(0);
T d = l.get(0);
l.add(d); //与上面的方法相比,插入一个范型数据是被允许的,相对灵活一些
l.add(n); //错误!!只可以插入范型数据,绝不可插入具体类型数据。
}

只要我们对形参添加了一定的约束条件,那么我们在传递实参的时候,对实参的严格约束就会降低一些。上述代码都指定了一个类Number,并用了extends关键字,因此,在传递实参的时候,凡是从Number继承的类组成的链表,均可以传递进去。但上面代码的注释中也说的很清楚,为了不出现运行时错误,编译器会对你调用的方法做严格的限制:凡是参数为范型的方法,一律不需调用!!

public static void test5(ArrayList<? super Integer> l) ...{
Integer n = new Integer(45);
l.add(n); //与上面使用extends关键字相反,往链表里面插入指定类型的数据是被允许的。
Object x = l.get(0); //从链表里取出一个数据仍然是被允许的,不过要赋值给Object对象。
l.add(x); //错误!!将刚刚取出的数据再次插入链表是不被允许的。
}

对实参的限制更改为:必须是指定类型的父类。这里我们指定了Integer类,那么实参链表的元素类型,必须是Number类及其父类。下面我们重点讨论一下上述代码的第四条语句,为什么将刚刚取出的数据再次插入链表不被允许??道理很简单,刚刚取出的数据被保存在一个Object类型的引用中,而链表的add方法只能接受指定类型Integer及其子类,类型不匹配当然不行。

//帮助函数
public static <T>void helperTest5(ArrayList<T> l, int index) ...{
T temp = l.get(index);
l.add(temp);
}

//主功能函数
public static void test5(ArrayList<? super Integer> l) ...{
Integer n = new Integer(45);
l.add(n);
helperTest5(l, 0); //通过帮助类,将指定的元素取出后再插回去。
}
上述两个函数结合的原理就是:利用Wildcard的super关键字来限制参数的类型(范型函数不支持super,要是支持的话就不用这么麻烦了),然后通过范型函数来完成取出数据的再存储。注意:

//1、不可以用一个本地类型(如int float)来替换范型
//2、运行时类型检查,不同类型的范型类是等价的(Pair<String>与Pair<Employee>是属于同一个类型Pair),
// 这一点要特别注意,即如果a instanceof Pair<String>==true的话,并不代表a.getFirst()的返回值是一个String类型
//3、范型类不可以继承Exception类,即范型类不可以作为异常被抛出
//4、不可以定义范型数组
//5、不可以用范型构造对象,即first = new T(); 是错误的
//6、在static方法中不可以使用范型,范型变量也不可以用static关键字来修饰
//7、不要在范型类中定义equals(T x)这类方法,因为Object类中也有equals方法,当范型类被擦除后,这两个方法会冲突
//8、根据同一个范型类衍生出来的多个类之间没有任何关系,不可以互相赋值
// 即Pair<Number> p1; Pair<Integer> p2; p1=p2; 这种赋值是错误的。
//9、若某个范型类还有同名的非范型类,不要混合使用,坚持使用范型类
// Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
// Pair rawBuddies = managerBuddies; 这里编译器不会报错,但存在着严重的运行时错误隐患

Java范型的更多相关文章

  1. Java范型随笔

    最近在帝都好无聊啊, 排遣寂寞就只有让自己不要停下来,不断的思考了 QWQ; 最近做ndk, java有点忘了,突然看到了一些java范型方面的问题, 踌躇了一会, 想着想着,决定还是写个随笔记录下来 ...

  2. Java范型学习笔记

    对于范型的使用或者说印象只有集合,其他地方即使使用过也不知道,反正就是只停留在List<E> Map<K, V>,最近刚好闲来无事,就找找资料学习一下:下列为个人学习总结,欢迎 ...

  3. java范型集合中的成员排序

    范型集合中的类是JsonObject,不是自定义类,如果是自定义类就直接取要比较的字段值. ArrayList<JSONObject> TList = new ArrayList<J ...

  4. 关于java范型

    1 范型只在编译阶段有效 编译器在编译阶段检查范型结果之后,就会将范型信息删除.范型信息不会进入运行时阶段. 泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型. 2 不能对确定的范型 ...

  5. Java范型之T extends Comparable&lt&semi;&quest; super T&gt&semi;

    在观察Java源码的时候,发现了这么一个写法T extends Comparable<? super T>.不禁纳闷为什么要这么写呢?有什么好处吗,extends和super在这里的作用着 ...

  6. 范型方法 &amp&semi; 范型参数 &amp&semi; 范型返回值

    Java范型类 public class FanXingClassTest { public static void main(String args[]){ Test<Integer> ...

  7. Java Comparator的范型类型推导问题

    问题 在项目中,有一处地方需要对日期区间进行排序 我需要以日期区间的开始日为第一优先级,结束日为第二优先级进行排序 代码 我当时写的代码如下: List<Pair<LocalDate, L ...

  8. Java数组协变与范型不变性

    变性是OOP语言不变的大坑,Java的数组协变就是其中的一口老坑.因为最近踩到了,便做一个记录.顺便也提一下范型的变性. 解释数组协变之前,先明确三个相关的概念,协变.不变和逆变. 一.协变.不变.逆 ...

  9. Java数据结构与算法分析-第一章(引论)-Java中的范型&lt&semi;T&comma;E&gt&semi;构件

    一.为什么需要使用范型? 官方的说法是:Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型. 泛型的本质 ...

随机推荐

  1. PostgreSQL-系统表、系统视图

    系统表显示的都是当前操作数据库下的信息,对象都来自当前数据库.因为不同的系统表都用不同名的字段来记录不同对象的oid,这个表引用那个表,那个表又引用另一个表,所以这些字段名不太好记. pg_class ...

  2. ie9 placeholder兼容

    .phcolor{ color:#999;}//css样式 function isPlaceholer(){ var input = document.createElement("inpu ...

  3. 使用Application Insights 做分析

    Application Insights on Windows Desktop apps, services and worker roles : https://azure.microsoft.co ...

  4. Eclipse启动时选择workspace设置

    由于一直习惯eclipse中只使用一个工作空间,所以一般在eclipse刚刚安装好后第一次启动时,我就钩上了弹出的工作空间选择的对话框中以后不再提示的钩选. 结果这次突然需要用到它的工作空间提示功能了 ...

  5. ASP&period;NET中的GridView自带的编辑更新功能

    string ConStr = ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].Connec ...

  6. Could not write to output file &&num;39&semi;c&colon;&bsol;Windows&bsol;Microsoft&period;NET&bsol;Framework64&bsol;v4&period;0&period;30319&bsol;Temporary ASP&period;NET Files&bsol;root&bsol;xx&&num;39&semi;

    1.清了C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files 2.给上述文件夹EveryOne和IIS_Use ...

  7. angularjs页面传参

    例如:路由配置如下: $stateProvider.state('admin.userList', { url: '/listUser?type&role',         //参数必须先在 ...

  8. nginx日志格式

    日志格式 log_format main '$remote_addr - $remote_user [$time_local] $request '                    '&quot ...

  9. scrollIntoView&lpar;&rpar; 调用元素就可以出现在视窗中

    /* 如果滚动页面也是DOM没有解决的一个问题.为了解决这个问题,浏览器实现了一下方法, 以方便开发人员如何更好的控制页面的滚动.在各种专有方法中,HTML5选择了scrollIntoView() 作 ...

  10. 计算基因上外显子碱基覆盖度(exon coverage depth):Samtool工具使用

    假设想要计算ATP1A4基因上的外显子碱基覆盖度 首先查询这个基因所有exon的起始和终止位置,查询链接:http://grch37.ensembl.org/Homo_sapiens/Transcri ...