java学习笔记10--泛型总结
集合类中可以存储各种数据,数据一旦存入,其类型均会转化为Object类型。从集合类中取出数据时,一般均需要将Object类型转换回存入之前的实际类型
Vector v=new Vector(); v.add("张三"); //存入字符串 String name=(String)v.get(0); //强制类型转换,OK v.add(new Date()); //存入当前时间对象,OK // 由于Date类型不能转换为String,下面语句会在运行时发生错误,但这种错误在编译时不会被检查出来 String date=(String)v.get(1); //编译器不会发现这里有问题
强类型集合
传统的集合类的实例中可以存储任意类型数据,这种集合类称为弱类型集合类。JDK1.5以后,引入了强类型集合类:
v.add("hello"); //加入的是字符串,OK String name=v.get(0); //取出时,无需做类型转换,如果想在这种强类型集合中加入日期数据,在编译时就会报告错误 v.add(new Date()); //编译器会直接报告类型不匹配错误
定义泛型(Generics)类
强类型集合采用了JDK1.5引入的泛型语法。泛型相当于类中一种特殊的类型,这种类型的特点是在实例化该类时可指定为某个具体的实际类型。
声明包含泛型的类的格式如下:
[访问修饰符] class 类名 泛型1,泛型2,… { 泛型1 泛型成员1; 泛型2 泛型成员2; //.... }
声明中的泛型1、泛型2等等泛型符号可以是任意合法的Java标识符。
泛型类的声明示例
//此处声明了一个包含泛型T的泛型类,T代表所有可能的类型,而T的实际类型在Generic类实例化时指定。 class Generic T { private T f; //f为泛型成员 public void setF(T f) {//setF方法的参数类型为泛型T this.f = f; public T getF() {//getF方法的返回类型为泛型T return f; }
泛型类的实例化
创建泛型类的实例时,可以使用一对尖括号指定泛型的真正类型
实例化时的泛型的默认类型
泛型类实例化时,并不一定要指明泛型对应的实际类型,此时会使用Object作为泛型的默认类型
Generic f3 = new Generic(); f3.setF(new Boolean(false));
编译时编译器会发出警告:
Note: Generic.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
建立类型为泛型类的数组
如果要建立泛型类的数组,需要注意new关键字后面不要加入泛型的实际类型名,如下所示:
Generic String [] gs; //声明泛型类的数组 //先对泛型数组进行初始化 gs = new Generic[5]; //不要写成new Generic String [5] //再分别为每一个数组元素进行初始化 gs[0] = new Generic String //为第一个数组元素赋值 //....
包含多个泛型的类定义示例
包含有两个泛型定义的类声明和实例化:
class Generic2 T1, T2 { private T1 f1; private T2 f2; //... //给出泛型T1, T2的实际类型 Generic Integer, Boolean f = new Generic Integer, Boolean //没有给出泛型T1, T2的实际类型 Generic f1 = new Generic(); //T1, T2将默认为是Object类型
泛型成员的使用
在泛型类中的泛型成员不能直接实例化,其实例必须要通过方法的参数传递给泛型成员:
class Generic T { private T[] array; //此处不能用new T[]实例化array public void setArray(T[] array) { this.array = array; public T[] getArray() { return array; }
测试程序:
public class test { public static void main(String args[ ]) { String[] strs = {"red", "black", "green"}; Generic String f = new Generic String //向泛型成员array传递实际的字符串数组 f.setArray(strs); //读取泛型成员array的值,将其赋给字符串数组变量strs strs = f.getArray(); }
泛型成员的可用方法
由于泛型类型只有在类实例化后才能确定,类中的泛型成员只能使用Object类型中的方法:
class Generic T { T f; void setF(T f){ this.f = f; } //.... void doSome(){ //getClass和toString都是Object中的方法 System.out.println(f.getClass().getName()); System.out.println(f.toString()); }
测试程序:
public class javatest { public static void main(String args[ ]) { String strs = "hello"; Generic String f = new Generic String f.setF(strs); f.doSome(); }限制泛型上限类型
extends关键字用来指定泛型的上限,在实例化泛型类时,为该泛型指定的实际类型必须是指定类的子类或指定接口的子接口
import java.util.List; public class ListGeneric T extends List { private T list; public void setList(T list) { this.list = list; public T getList() { return list; }
在限定泛型的类型时,无论要限定的是接口或是类,都要使用extends关键词
测试例子:
ListGeneric Vector f1 = new ListGeneric Vector ListGeneric ArrayList f2 = new ListGeneric ArrayList
如果不是List的类型,编译时就会发生错误:
ListGeneric HashMap f3 = new ListGeneric HashMap type parameter java.util.HashMap is not within its bound ListGeneric HashMap f3 = new ListGeneric HashMap
默认的泛型限制类型
定义泛型类别时,如果只写以下代码:
class Generic T { //... }
相当于下面的定义方式:
class Generic T extends Object { //... }
限定泛型上限后的成员可用方法:
泛型类型的上限一经限定,类中的泛型成员就可使用上限类型中的方法和其他可用成员:class ListGeneric T extends List { private T list; public void setList(T list) { this.list = list; public void doSome() { //ad、get方法都是List接口中定义的方法 list.add(new Integer(0)); System.out.println(list.get(0)); }
Object是所有类的父类,因此,所有的类型的实例都可赋值给声明为Object类型的变量
Boolean f1 = new Boolean(true); Integer f2 = new Integer(1); Object f = f1; //ok f = f2; //ok
在实例化泛型类时,将泛型指定为Object类型却不存在着和其他类型之间的兼容性:
Generic Boolean f1 = new Generic Boolean Generic Integer f2 = new Generic Integer Generic Object f=f1; //f1和f类型并不兼容,发生编译错误 f=f2; //f2和f类型同样不兼容,也会发生编译错误泛型通配字符(Wildcard)
泛型类实例之间的不兼容性会带来使用的不便。使用泛型通配符(?)声明泛型类的变量可以解决这个问题
Generic Boolean f1 = new Generic Boolean Generic Integer f2 = new Generic Integer Generic Object f3 = new Generic Object Generic ? f = f1; //ok f = f2; //ok f = f3; //ok
通配符也可以用于方法的参数类型的声明,表示该参数可接受对应泛型类型的任意实例。
以下类定义中的printCollection方法可以打印任意强类型集合中的内容
class test { //Collection ? 可以匹配任意强类型的集合 static void printCollection(Collection ? c) { for(Object o : c) System.out.println(o); }
和限制泛型的上限相似,同样可以使用extends关键字限定通配符匹配类型的上限:
Generic ? extends List f = null; f = new Generic ArrayList //ok //... f = new Generic Vector //ok //... //以下语句会发生编译错误,因为HashMap没有实现List接口 f = new Generic HashMap incompatible types found : Generic java.util.HashMap required: Generic ? extends java.util.List f = new Generic HashMap
限定通配符匹配类型的下限
还可以使用super关键词将通配符匹配类型限定为某个类型及其父类型:
//将f限定为只能代表采用java.sql.Date的父类实例化的 Generic ? super java.sql.Date f = null; f = new Generic java.sql.Date //ok //OK,java.util.Date是java.sql.Date的父类 f = new Generic java.util.Date //错误,因为String不是java.sql.Date的父类 f = new Generic String
不仅类可以声明泛型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法。其定义格式为:
访问修饰符 泛型列表 返回类型 方法名(参数列表){
实现代码
}
其中泛型列表为用逗号分隔的合法Java标识符。
在泛型列表中声明的泛型,可用于该方法的返回类型声明、参数类型声明和方法代码中的局部变量的类型声明。类中其他方法不能使用当前方法声明的泛型。使用泛型方法可以解决上述的泛型通配符造成的问题
泛型方法声明示例:
class cc{ 方法fun声明了一个泛型T,该方法将任意类型的数组a中的所有 元素复制到相应的强类型集合c当中而不会导致编译错误。 此处的泛型声明T仅作用于fun方法的声明部分和实现代码部分。 public static T void fun(T[] a, Collection T c){ for(T o : a) c.add(o); //不会出现类似通配符的编译错误 }
调用泛型方法和调用普通方法没有任何不同,只需要传递含有具体类型的实参即可:
泛型方法的调用示例:
//对cc中定义的泛型方法fun进行调用测试 public class javatest { public static void main(String args[ ]) { String[] sa = new String[100]; Collection String cs = new Vector String Collection Object co = new Vector Object cc.fun(sa, cs); //fun中的泛型T此时匹配类型String cc.fun(sa, co); //fun中的泛型T此时匹配类型Object }
限定泛型方法中泛型类型
泛型方法中的声明的泛型,同样可以使用extends关键字限定其类型的下限:
class cc{ //限定aToC方法中的泛型T必须是实现了序列化接口的类型 public static T extends java.io.Serializable void fun(T[] a,Collection T c){ for(T o : a) c.add(o); }原始类型和向后兼容
先看一个泛型类定义
public class GenericStack E { ArrayList E list = new ArrayList E public int getSize() { return list.size(); public E peek() { return list.get(getSize() - 1); public void push(E o) { list.add(o); public E pop() { E o = list.get(getSize() - 1); list.remove(getSize() - 1); return o; public boolean isEmpty() { return list.isEmpty(); }
可以使用泛型类而无需指定具体类型:
GenericStack stack = new GenericStack(); //大体等价于于下面的语句 GenericStack Object stack = new GenericStack Object
再看下面的一个例子:
class Max { public static Comparable max(Comparable o1, Comparable o2) { if(o1.compareTo(o2) 0) return o1; return o2; public class javatest { public static void main(String args[]) { Max.max("welcome", 12); }
得到一个运行错误:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
结论:原始类型是不安全的,尽量使用泛型类型
一个更好的编写max方法的方式是使用泛型:
class Max { public static E extends Comparable E E max(E o1, E o2) { if(o1.compareTo(o2) 0) return o1; return o2; }
再次调用上面的命令就会显示一个编译错误,由于max方法的两个参数必须是相同的类型
继承中的泛型继承时如需保留父类泛型,需要在声明时加入父类泛型
class subGeneric T1, T2, T3 extends Generic T1, T2 { private T3 f3; public void setF3(T3 f3) { this.f3 = f3; public T3 getF3() { return f3; }
如果不保留父类中的泛型声明,则继承下来的T1与T2自动变为Object类型。建议父类中的泛型声明在子类中都要保留
继承时指定父类的泛型类型
public class SubGeneric T3 extends Generic String, Object { private T3 f3; public void setF3(T3 f3) { this.f3 = f3; public T3 getF3() { return f3; }
接口也可包含泛型的声明:
interface I T1, T2 { T1 getT1(); T2 getT2(); //... }
实现泛型接口时,类在定义时可以不声明泛型接口中的泛型,此时接口中的泛型也会自动变为Object类型:
class IC implements I { public Object getT1() { } public Object getT2() { } //... }
泛型接口的实现
class IC T1, T2 implements I T1, T2 { public T1 getT1() { } public T2 getT2() { } //... I String, Integer i = new IC String, Integer
实现泛型接口时指定泛型类型
在实现泛型接口时,也可直接指定接口中的泛型的实际类型:
interface I T1, T2 { T1 getT1(); T2 getT2(); //... //实现接口I时,直接指定泛型T1、T2的类型 class IC implements I String, Integer { //由于指定接口I中T1类型为String,getT1返回类型必须为String public String getT1() { } //由于指定接口I中T2类型为Integer,getT2返回类型必须为Integer public Integer getT2() { } //... }泛型和枚举
由于枚举类型不能直接实例化,所以枚举的定义中不能含有泛型的声明,但枚举中可包含泛型方法的定义。
public enum TrafficLight{ Red,Amber,Green; private int duration; public static T void avgDuration(Collection T carType){ //.... //.... }
「 Java基础-泛型 」一篇文章说清楚Java泛型中的通配符T、E、K、V、N、?和Object的区别与含义 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型。 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。 可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。
Java中的泛型 泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数 可以声明泛型类、泛型方法和泛型接口(下一章介绍接口)
Java基础之泛型程序设计 类型变量使用大写形式,且比较短,在Java库中,使用变量E表示集合的元素类型,K和V分别表示表的关键字与值得类型。Object 表示”任意类型”
相关文章
- Java笔记:IO
- JVM深入学习笔记一:Java 编译器初探
- java学习笔记5--类的方法
- java学习笔记11--集合总结
- java学习笔记16--I/O流和文件
- 【JAVA】毕向东Java基础视频教程-笔记
- [Linux] Install java and add JAVA_HOME, PATH
- JAVA笔记-如何将百万级数据高效的导出到Excel表单
- java.lang.OutOfMemoryError: Java heap space 解决方法
- Java多线程学习笔记 - 八、Java中的wait和sleep的区别
- Atitit 搜索蓝牙设备 powershell的实现 java noede.js python 先用脚本语言python nodejs,不好实现。。Java 也不好实现。。 Netcore可以,
- java反射详解
- 【java】Java 包(package)
- 疯狂Java学习笔记(70)-----------挚爱Java
- Java学习笔记六(I/O流)
- JAVA学习笔记 -- JDBC及其应用
- Java程序猿JavaScript学习笔记(4——关闭/getter/setter)
- Java程序猿从笨鸟到菜鸟之(九十二)深入java虚拟机(一)——java虚拟机底层结构具体解释
- Java开发技术之成为高级java工程师必须学习的三个技术
- Java学习笔记(五)——数组
- java学习笔记)StringBuilder类
- java学习笔记——继承