泛型不用考虑对象的具体类型。优点在于,因为不用考虑对象的具体类型所以可以对一类对象执行一定的相同操作;缺点在于,因为没有考虑对象的具体类型所以就不能使用对象自带的接口函数。泛型的最佳用同是实现容器类。在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<? super T>

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

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

    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中的范型<T,E>构件

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

随机推荐

  1. WPF:动态显示或隐藏Listview的某一列

    这几天做项目,需要做个listview满足能够动态显示或隐藏某些列,由于自己是菜鸟水平,查了两天资料也没有想出解决办法.就在我山穷水尽的时候看到了Mgen的一篇博客,给了我很大启发,所以我也决定把自己 ...

  2. 2238"回文字串"报告

    题目描述: 回文串,就是从前往后和从后往前看都是一样的字符串.那么现在给你一个字符串,请你找出该字符串中,长度最大的一个回文子串. 输入描述: 有且仅有一个仅包含小写字母的字符串,保证其长度不超过50 ...

  3. Randoop测试类和方法(用例自动生成)

    详细使用方法见randoop官网: https://randoop.github.io/randoop/manual/index.html 测试程序之前,先检测下你的Randoop是否配置好: 打开c ...

  4. Excel多表合并的宏

    Sub 合并当前目录下所有工作簿的全部工作表() Dim MyPath, MyName, AWbName Dim Wb As Workbook, WbN As String Dim G As Long ...

  5. java中变量赋值的理解

    1.当赋值的值超出声明变量的范围时候,会报错! byte a =200 //会报错,因超出范围. byte a =(byte)200;//进行一个强制转换,就不会报错,不过会超出范围,超出部分会从头开 ...

  6. USB的前世今生

    在人类的历史长河中,很少有一种技术或者传输标准能像USB那样跟我们的生活息息相关,甚至到了没有不行的地步.USB对于今天的人们来说,就好像是空气,是水,是我们每天必需但是又熟视无睹的东西,没有多少人知 ...

  7. 初涉算法——C++

    一.sstream头文件运用 题目:输入数据的每行包括若干个(至少一个)以空格隔开的整数,输出每行中所有整数之和. #include<iostream> #include<cstri ...

  8. python全栈阶段测试(一)

    1.执行Python脚本的两种方式 如果想要永久保存代码,就要用文件的方式 如果想要调试代码,就要用交互式的方式 2.Pyhton单行注释和多行注释分别用什么? 单行注释:# 多行注释: '' &qu ...

  9. 【linux相识相知】磁盘分区及文件系统管理详解

    磁盘,提供持久的数据存储,它不像我们的内存,如果突然断电了,在内存中的数据一般都会被丢掉了,内存中的数据在保存的时候,会被写到硬盘里面,磁盘也是一种I/O设备. 我们都知道磁盘分区完成之后,还要进行格 ...

  10. juquery 中 size()和length的区别 以及优缺点

    size()是jQuery提供的函数,而length是属性(不带括号).jQuery提供的源代码是这样的:size: function() {return this.length;}其实也就是说,在j ...