一个泛型类就是具有一个或者多个类型变量的类。

我们可以只关注泛型,而不会为数据存储的细节而烦恼 。

java泛型(一)、泛型的基本介绍和使用 http://blog.csdn.net/lonelyroamer/article/details/7864531

泛型的内部原理:类型擦除以及类型擦除带来的问题 http://blog.csdn.net/lonelyroamer/article/details/7868820

java泛型(三)、通配符的使用 http://blog.csdn.net/lonelyroamer/article/details/7927212

1.泛型类

一个Pair类:

public class Pair<T>{
private T first ;
private T second; public Pair(){ first = null; second = null; }
public Pair(T first , T second){ this.first=first;this.second=second; } public T getFirst(){ return first; }
public T getSecond(){ return second; } public void setFirst(T first){ this.first = first; }
public void setSecond(T second){ this.second = second; } public void pPrint(){
System.out.println(getFirst()+" " +getSecond());
}
}

注:泛型类可有多个类型变量如  public class Pair<T,U>{...}

//演示泛型类
public class PairDemo {
public static void main(String[] args) {
Pair<String> p1 = new Pair();
System.out.println(p1.getFirst()+" "+p1.getSecond());
Pair<String> p2 = new Pair("lee","lei");
//写成Pair p2 = new Pair("lee","lei") 也可以
System.out.println(p2.getFirst()+" "+p2.getSecond()); String[] words = {"hello" , "world" , "super","man"};
Pair<String> p3 = ArrayAlg.minmax(words); //获得最大、最小的字符串组成的键值对
p3.pPrint();
}
}
class ArrayAlg{
public static Pair<String> minmax(String[] s_array){
if(s_array == null||s_array.length==0)
return null;
String min = s_array[0];
String max = s_array[0];
for(int i = 1 ; i < s_array.length ; i++){
if(min.compareTo(s_array[i]) > 0 ) min = s_array[i];
if(max.compareTo(s_array[i]) < 0 ) max = s_array[i];
}
return new Pair<String> (min,max);
}
}

运行结果:

null null
lee lei
hello world

2.泛型方法

泛型方法可以定义在普通类中,也可以定义在泛型类中。这个方法是定义在普通类中的。

class ArrayAlg{

	//方法返回中间位置元素
public static <T> T getMiddle(T[] s_array){
if(s_array == null||s_array.length==0)
return null;
return s_array[s_array.length/2]; }
}

调用泛型方法时,在方法名前尖括号内放入具体的类型。

但大多数情况下,可以省略,编译器有足够的信息推断出所调用的方法。

public class PairMethodDemo {
public static void main(String[] args) {
String[] words = {"hello" , "world","baidu" , "super","man"};
String s_middle = ArrayAlg.<String>getMiddle(words); //可写成ArrayAlg.getMiddle(words);
System.out.println("位置在 中间的元素:"+s_middle); /* 注意 不能用int[] nums = {...};
* 泛形要求能包容的是对象类型
* 而基本类型在java里不属于对象
* 因此,想要使用基本类型就用其包装类实现功能;
* */
Integer [] nums = {2,52,6,8,4,2,6,10,7};
Integer n_middle = ArrayAlg.getMiddle(nums);
System.out.println("位置在中间的数字元素:"+n_middle);
}
}  

运行结果:

位置在 中间的元素:baidu
位置在中间的数字元素:4

3.变量类型的限定

有时,类或方法需要对类型变量T加以约束,例子:

class ArrayAlg{
public static <T> T min(T[] arr){//编译错误
if(arr==null||arr.length==0){
return null;
}
T smallest = arr[0];
for(int i = 0 ; i < arr.length ; i++){
if(smallest.compareTo(arr[i])>0)
smallest = arr[i];
}
return smallest;
}

  这个例子存在一个问题:smallest类型为 T, 这意味着它可以为任意类型的对象,但是T类型的对象不一定有compareTo方法。

解决方法:通过对类型变量T设置限定.

我们知道所有实现Comparable接口的类都会有compareTo方法,所以可以对T做如下限定:

public static <T extends Comparable> T min(T[] arr){...}

现在,泛型的min方法只能被实现了Comparable接口的类(如String、Date等)的数组调用。

注:

1、不管该限定是类还是接口,统一都使用关键字 extends

2、可以使用&符号给出多个限定,比如

注:为什么使用extends而不使用implements

T和绑定类型可以使类,也可以是接口,选择extends关键字的原因是更接近子类的概念。

并且Java的设计者不打算在语言中添加一个新的关键字。

一个变量类型或者通配符可以有多个限定: T extends Comparable & Serializable

约束与局限性:

(1)不能用基本类型实例化类型参数(不能用类型参数代替基本类型)

(2)运行时类型查询制适用于原始类型
如:

if(a instanceof Pair<String>)  //ERROR

实际上仅仅测试a是否是任意类型的一个Pair。下面的测试同样如此

if(a instanceof Pair<T>)  //ERROR

或强制类型转换:

Pari<String> p = (Pari<String>) a;  //Warring,can only test "a" is a "Pair"

要记住这一风险,无论何时使用instanceof或涉及泛型类型的强制类型转换表达式都会看到一个编译器警告。

同样的道理,getClass方法总是返回原始类型。例如:

Pair<String> stringPair = ...;

Pari<Employee> employeePair = ...;

if(stringPair.getClass()==employeePair.getClass())//they are equal

(3)不能创建参数化类型的数组

如:Pair<String>[] table = new Pair<String>[10]  //ERROR

这有什么问题呢?擦除之后,table的类型是Pair[]。可以把它转换为Object[];

Object[] objarry = table;

数组会记住它的元素类型,如果试图存储其他类型的元素,就会抛出一个ArrayStoreException异常;

objarry[0] =new Pair<Employee>();能够通过数组存储检查,不过仍会导致一个类型错误。出于这个原因,不允许创建参数化类型的数组。

需要说明的是,只是不允许创建这些数组,而生命类型为Pari<String>[]的变量仍然是合法的。不过不能用new Pair<String>[10]初始化这个变量。

如果需要手机参数化类型对象,只有一种安全而有效的方法:使用ArrayList: ArrayList<Pair<String>>.

(4)Varargs警告

问题:向参数个数可变的方法传递一个泛型类型的实例。

考虑下面的方法,它的参数个数是可变的

public static <T> void addAll(Collection<T> coll , T... ts){
for(T t : ts) coll.add(t);
}

为了调用这个方法,Java虚拟机必须建立一个Pair<String>数组,这就违反了前面的规则。

不过对于在这种情况,规则有所放松,只会得到一个警告,而不是错误。

调用的例子:

public class VarArgs {

	public static void main(String[] args) {
List<Pair<String>> table = new ArrayList();
Pair<String> pair1 = new Pair("S1First","S1Second");
Pair<String> pair2 = new Pair("S2First","S2Second");
addAll(table,pair1,pair2);
//上一行代码有警告:Type safety: A generic array of Pair<String> is created for a varargs parameter
for(int i = 0 ; i < table.size() ; i ++){
table.get(i).pPrint();
}
}
}

运行结果:

S1First S1Second
S2First S2Second

(5)不能实例化类型变量

不能使用像new T(...),new T[...]或T.class这样的表达式中的类型变量。

如下面的Pair<T>构造器就是违法的:

publoic Pair(){ first = new T() ;seconde = new T(); }  //ERROR;

类型擦除将T改变成Object,且本意肯定不希望调用 new Object().

但是可以通过反射调用Class.newInstance方法来构造泛型对象。

 

(6)不能抛出或捕获泛型类的实例

拓展Throwable也是不合法的

以下定义不能正常编译:

public class Problem<T> extends Exception{/* ... */} //ERROR

catch子句中不能使用类型变量。

public static <T extends Throwable> void doWork(Class<T> t){
  try{
    do work
  }catch(T e){ //ERROR can't catch type variable
  }
}

  

(7)注意擦除后的冲突

当泛型类型被擦除时,无法创建引发冲突的条件。

public class Pait<T>{
  public boolean equals(T value){
    return first.equals(value)&&second.equals(value);
  }
  .....
}

考虑一个Pair <String>。从概念上讲,它有两个equals方法:

boolean equals(String)//defined in Pair<T>

boolean equals(Object)//inherited from Object

但是,直觉吧我们引入歧途。

方法擦除 boolean equals(T)

就是 boolean equals(Object)

与Object.equals冲突

补救的方法是重命名引发错误的方法。

Java学习笔记--泛型的更多相关文章

  1. Java学习笔记——泛型

    假定T不仅要指定接口的类继承.使用下面的方式: public class some<T extends Iterable<T> & Comparable<T>&g ...

  2. Thinking in Java学习笔记-泛型和类型安全的容器

    示例: public class Apple { private static long counter; private final long id = counter++; public long ...

  3. Java 学习笔记 泛型

    泛型 上界匹配 ? extends Number 下界匹配 ? super Number getSimpleName 不包括包名 getName 会包括包名 常和反射联合使用,做框架 Type是一个标 ...

  4. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

  5. Java学习笔记4

    Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...

  6. java学习笔记08--泛型

    java学习笔记08--泛型 泛型可以解决数据类型的安全性问题,它主要的原理,是在类声明的时候通过一个标识标识类中某个属性的类型或者是某个方法的返回值及参数类型.这样在类声明或实例化的时候只要指定好需 ...

  7. java学习笔记10--枚举

    java学习笔记10--枚举 在JDK1.5之前,java可以有两种方式定义新类型:类和接口.对于大部分面向对 象编程来说,这两种方法看起来似乎足够了,但是在一些特殊情况下,这些方法就不适合.例如,想 ...

  8. java学习笔记10--泛型总结

    java学习笔记系列: java学习笔记9--内部类总结 java学习笔记8--接口总结 java学习笔记7--抽象类与抽象方法 java学习笔记6--类的继承.Object类 java学习笔记5-- ...

  9. 20145230《java学习笔记》第九周学习总结

    20145230 <Java程序设计>第9周学习总结 教材学习内容 JDBC JDBC简介 JDBC是用于执行SQL的解决方案,开发人员使用JDBC的标准接口,数据库厂商则对接口进行操作, ...

随机推荐

  1. QT中读取文本数据(txt)

    下面的代码实现读取txt文档中的数据,并且是一行一行的读取. void MainWindow::on_pushButton_clicked() { QFile file("abcd.txt& ...

  2. dos下修复硬盘损坏的文件

    点击开始-->运行-->输入cmd,出现DOS状态对话框.在光标处输入有损坏文件的磁盘盘符后回车(如文件夹在D盘就输入D:然后回车),再输入“CHKDSK”,回车即可看到相关检测信息.“C ...

  3. Express4 Route笔记

    可以参考Express官网关于路由一节:http://expressjs.com/guide/routing.html 1:通过使用GET.POST方式定义主页路由,app.js: var expre ...

  4. 通过布局文件来显示ListView内容并注册 ListView事件

    1:layout/vlist.xml是我们的布局文件,在这里一定要对首节点加上 android:descendantFocusability="blocksDescendants" ...

  5. CentOs6.5中安装和配置vsftp简明

    这篇文章主要介绍了CentOs6.5中安装和配置vsftp简明教程,需要的朋友可以参考下     一.vsftp安装篇 复制代码代码如下: # 安装vsftpdyum -y install vsftp ...

  6. Activity切换效果(overridePendingTransition)

    在Android开发过程中,经常会碰到Activity之间的切换效果的问题,下面介绍一下如何实现左右滑动的切换效果,首先了解一下Activity切换的实现,从Android2.0开始在Activity ...

  7. poj2429:因数分解+搜索

    题意:给定gcd(a,b)和lcm(a,b) 求使得a+b最小的 a,b 思路:结合算数基本定理中 gcd lcm的质因子表示形式 把lcm(a,b)质因数分解 以后 通过dfs找到 a+b最小的a ...

  8. puppetSvn集成

  9. itoa的源代码实现

    由于通过socket传递数据的时候,仅仅能够通过字符串类型,可是,当我们要传递的数据是整型的是,应该怎么办呢?本来我想着使用for循环,可是,总感觉太麻烦了,后来别人告诉我能够使用itoa,以下是it ...

  10. extern C的作用详解

    extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码.加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C+ ...