Java笔记(一)——泛型
来源:互联网 发布:天马运动网络分销平台 编辑:程序博客网 时间:2024/04/26 10:52
泛型类
泛型类是具有一个或多个类型变量的类。从字面上可以看出泛型类是具有类型参数的类,使得同一个类型或方法可以被很多不同的类型所复用。下面是使用Mybatis作为ORM框架时自定义的一个BaseDAO类。实体类类型非常多不可能为每一个实体类单独写数据库相关操作,为了复用给BaseDAO加上了参数类型T,使其可以适用所有实体类型。
import java.util.ArrayList;import java.util.List;import java.util.Map;import org.mybatis.spring.SqlSessionTemplate;public class BaseDAO<T> {private SqlSessionTemplate sqlSession; public SqlSessionTemplate getSqlSession() { return sqlSession; } public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } @SuppressWarnings("unchecked") public T selectOne(String statement,Object parameter) { T obj = null; try{ obj = (T)sqlSession.selectOne(statement, parameter); } catch (Exception e) { e.printStackTrace(); } return obj; } public Object selectOneObject(String statement,Object parameter) { Object obj = null; try { obj = (Object)sqlSession.selectOne(statement, parameter); } catch (Exception e) { e.printStackTrace(); } return obj; } public Map<String, T> selectStringKeyMap(String statement, Object parameter, String mapKey) { Map<String,T> map = sqlSession.selectMap(statement, parameter, mapKey); return map; } public Map<Integer, T> selectIntKeyMap(String statement, Object parameter, String mapKey) { Map<Integer,T> map = sqlSession.selectMap(statement, parameter, mapKey); return map; } @SuppressWarnings("unchecked") public List<T> selectList(String statement, Object parameter) { List<T> result = null; try { result = (List<T>)sqlSession.selectList(statement, parameter); } catch (Exception e) { e.printStackTrace(); } return result; } public List<Object> selectObjectList(String statement, Object parameter) { List<Object> result = null; result = (List<Object>)sqlSession.selectList(statement, parameter); return result; } public List<String> selectStringList(String statement, Object parameter) { List<String> result = new ArrayList<String>(); List<Object> objs = null; objs = sqlSession.selectList(statement,parameter); for(Object obj : objs) { result.add((String)obj); } return result; } public int selectInt(String statement,Object parameter) { Object obj = sqlSession.selectOne(statement, parameter); return obj == null ? 0 : (Integer)obj; }}
泛型方法
泛型方法是具有类型参数的方法,可以在普通类中定义泛型方法。定义泛型方法时类型变量放在修饰符后返回变量前。
public <T extends Comparable<T>> T min(T one, T two) { if(one.compareTo(two) > 0) { return two; } return one;}在调用泛型方法时一般需要在方法名前用尖括号指明具体类型,但是一般编译器能够通过参数信息推断出具体类型,因此可以省略。
test.<String>min("one", "two");test.min("one", "two");编译器可以通过参数"one","two"推断出具体类型为String。
类型变量
类型变量可以通过extends来添加边界约束。如泛型方法示例中的T extends Comparable就限定了该方法的参数类型必须是实现了Comparable接口的类型。因为如果没有实现Comparable接口那么就不能保证类型中含有compareTo方法。在这里Comparable被称为限定类型(Bound Type)。一个类型变量可以由多个限定变量约束,如T extends Comparable & Serializable。注意如果限定类型中有类那么必须写在第一个位置,在接口前面。如Test是一个普通类。
@SuppressWarnings("unchecked") public <T extends Test & Comparable<T>> T min(T one, T two) { //正确 if(one.compareTo(two) > 0) { return two; } return one; }@SuppressWarnings("unchecked") public <T extends Comparable<T> & Test> T min(T one, T two) {//错误:The type Test is not an interface; it cannot be specified as a bounded parameter if(one.compareTo(two) > 0) { return two; } return one; }通配符类型
通配符类型形如
? extends Employee? super Worker先看以下类图
使用通配符类型可以添加extends和super限定,相当于是增加了一个类型层次,比起固定的参数类型GenericTypeExample<T>更加具有灵活性。GenericTypeExample<? extends Employee> gte; 可以赋值为GenericTypeExample<Manager>的对象或者GenericTypeExample<Worker>的对象。
类型擦除
在虚拟机中所有类型都是普通类型,不存在泛型类型的对象。Java是通过类型擦除机制将泛型类型转变为普通类型。擦除(Erased)是将泛型类型转变为普通类型,首先将原始类型(Raw Type)后的类型参数去掉只保留原始类型。然后使用限定类型(Bound Type)替换所有类型参数,如果没有限定类型则用Object替换。以下代码定义了一个泛型类型GenericTypeExample<T>,对应的原始类型为GenericTypeExample。
public class GenericTypeExample<T> { private T field1; private T field2; public GenericTypeExample() { } public GenericTypeExample(T field1, T field2) { this.field1 = field1; this.field2 = field2; } public T getField1() { return field1; } public void setField1(T field1) { this.field1 = field1; } public T getField2() { return field2; } public void setField2(T field2) { this.field2 = field2; }}
将 GenericTypeExample进行反编译结果如下:
D:\>javac GenericTypeExample.javaD:\>javap -verbose GenericTypeExampleCompiled from "GenericTypeExample.java"public class GenericTypeExample extends java.lang.Object Signature: length = 0x2 00 19 SourceFile: "GenericTypeExample.java" minor version: 0 major version: 50 Constant pool:const #1 = Method #5.#28; // java/lang/Object."<init>":()Vconst #2 = Field #4.#29; // GenericTypeExample.field1:Ljava/lang/Object;const #3 = Field #4.#30; // GenericTypeExample.field2:Ljava/lang/Object;const #4 = class #31; // GenericTypeExampleconst #5 = class #32; // java/lang/Objectconst #6 = Asciz field1;const #7 = Asciz Ljava/lang/Object;;const #8 = Asciz Signature;const #9 = Asciz TT;;const #10 = Asciz field2;const #11 = Asciz <init>;const #12 = Asciz ()V;const #13 = Asciz Code;const #14 = Asciz LineNumberTable;const #15 = Asciz (Ljava/lang/Object;Ljava/lang/Object;)V;const #16 = Asciz (TT;TT;)V;const #17 = Asciz getField1;const #18 = Asciz ()Ljava/lang/Object;;const #19 = Asciz ()TT;;const #20 = Asciz setField1;const #21 = Asciz (Ljava/lang/Object;)V;const #22 = Asciz (TT;)V;const #23 = Asciz getField2;const #24 = Asciz setField2;const #25 = Asciz <T:Ljava/lang/Object;>Ljava/lang/Object;;const #26 = Asciz SourceFile;const #27 = Asciz GenericTypeExample.java;const #28 = NameAndType #11:#12;// "<init>":()Vconst #29 = NameAndType #6:#7;// field1:Ljava/lang/Object;const #30 = NameAndType #10:#7;// field2:Ljava/lang/Object;const #31 = Asciz GenericTypeExample;const #32 = Asciz java/lang/Object;{public GenericTypeExample(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 6: 0 line 8: 4public GenericTypeExample(java.lang.Object, java.lang.Object); Code: Stack=2, Locals=3, Args_size=3 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: aload_0 5: aload_1 6: putfield #2; //Field field1:Ljava/lang/Object; 9: aload_0 10: aload_2 11: putfield #3; //Field field2:Ljava/lang/Object; 14: return LineNumberTable: line 10: 0 line 11: 4 line 12: 9 line 13: 14 Signature: length = 0x2 00 10public java.lang.Object getField1(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: getfield #2; //Field field1:Ljava/lang/Object; 4: areturn LineNumberTable: line 16: 0 Signature: length = 0x2 00 13public void setField1(java.lang.Object); Code: Stack=2, Locals=2, Args_size=2 0: aload_0 1: aload_1 2: putfield #2; //Field field1:Ljava/lang/Object; 5: return LineNumberTable: line 20: 0 line 21: 5 Signature: length = 0x2 00 16public java.lang.Object getField2(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: getfield #3; //Field field2:Ljava/lang/Object; 4: areturn LineNumberTable: line 24: 0 Signature: length = 0x2 00 13public void setField2(java.lang.Object); Code: Stack=2, Locals=2, Args_size=2 0: aload_0 1: aload_1 2: putfield #3; //Field field2:Ljava/lang/Object; 5: return LineNumberTable: line 28: 0 line 29: 5 Signature: length = 0x2 00 16}从反编译的结果可以看出,GenericTypeExample编译后的类型为public class GenericTypeExample extends java.lang.Object。其中所有出现T的地方均被替换为Object(如果T有限定类型则会被替换为第一个限定类型)。
public class Test { public static void main(String[] args) { GenericTypeExample<Test> gte = new GenericTypeExample(new Test(), new Test()); Test t = gte.getField1(); //取第一个属性 gte.setField2(new Test()); //为第二个属性赋值 }}
D:\>javap -verbose TestCompiled from "Test.java"public class Test extends java.lang.Object SourceFile: "Test.java" minor version: 0 major version: 50 Constant pool:const #1 = Method #8.#17; // java/lang/Object."<init>":()Vconst #2 = class #18; // GenericTypeExampleconst #3 = class #19; // Testconst #4 = Method #3.#17; // Test."<init>":()Vconst #5 = Method #2.#20; // GenericTypeExample."<init>":(Ljava/lang/Obje ct;Ljava/lang/Object;)Vconst #6 = Method #2.#21; // GenericTypeExample.getField1:()Ljava/lang/Ob ject;const #7 = Method #2.#22; // GenericTypeExample.setField2:(Ljava/lang/Obj ect;)Vconst #8 = class #23; // java/lang/Objectconst #9 = Asciz <init>;const #10 = Asciz ()V;const #11 = Asciz Code;const #12 = Asciz LineNumberTable;const #13 = Asciz main;const #14 = Asciz ([Ljava/lang/String;)V;const #15 = Asciz SourceFile;const #16 = Asciz Test.java;const #17 = NameAndType #9:#10;// "<init>":()Vconst #18 = Asciz GenericTypeExample;const #19 = Asciz Test;const #20 = NameAndType #9:#24;// "<init>":(Ljava/lang/Object;Ljava/lang/Object ;)Vconst #21 = NameAndType #25:#26;// getField1:()Ljava/lang/Object;const #22 = NameAndType #27:#28;// setField2:(Ljava/lang/Object;)Vconst #23 = Asciz java/lang/Object;const #24 = Asciz (Ljava/lang/Object;Ljava/lang/Object;)V;const #25 = Asciz getField1;const #26 = Asciz ()Ljava/lang/Object;;const #27 = Asciz setField2;const #28 = Asciz (Ljava/lang/Object;)V;{public Test(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 2: 0public static void main(java.lang.String[]); Code: Stack=5, Locals=3, Args_size=1 0: new #2; //class GenericTypeExample 3: dup 4: new #3; //class Test 7: dup 8: invokespecial #4; //Method "<init>":()V 11: new #3; //class Test 14: dup 15: invokespecial #4; //Method "<init>":()V 18: invokespecial #5; //Method GenericTypeExample."<init>":(Ljava/lang/Obj ect;Ljava/lang/Object;)V 21: astore_1 22: aload_1 23: invokevirtual #6; //Method GenericTypeExample.getField1:()Ljava/lang/O bject; 26: checkcast #3; //class Test 29: astore_2 30: aload_1 31: new #3; //class Test 34: dup 35: invokespecial #4; //Method "<init>":()V 38: invokevirtual #7; //Method GenericTypeExample.setField2:(Ljava/lang/Ob ject;)V 41: return LineNumberTable: line 5: 0 line 6: 22 line 7: 30 line 8: 41}
如果在声明GenericTypeExample时不添加类型参数,如图中所示,编译器不会自动添加Test的强制类型转换,报错"Type mismatch: cannot convert from Object to Test”。
- 在虚拟机内不存在泛型类型
- 擦除后编译器会在存取泛型域的方法基础上加上强制类型转换。从这里可以看出虽然编译器会擦除泛型类型但是仍然具有泛型类型的记忆
public class GenericTypeExample<T> { private T field1; private T field2; public GenericTypeExample() { } public GenericTypeExample(T field1, T field2) { this.field1 = field1; this.field2 = field2; } public void setField1(T field1) { System.out.println("super class method"); this.field1 = field1; }}public class SubGenericTypeExample extends GenericTypeExample<Test> { @Override public void setField1(Test field1) { System.out.println("sub class method"); }}
SubGenericTypeExample中覆写了父类中的setField1(T field1);从上面的擦除部分可以知道在类GenericTypeExample中方法会变为public void setField1(Object field1);由于SubGenericTypeExample是子类,所以也继承了该方法。但是SubGenericTypeExample本身继承的类型为GenericTypeExample<Test>并且覆写了setField1方法为public void setField1(Test field1);该方法与public void setField1(Object field1);相比参数类型不同,完全是两个方法,由此就产生了冲突。
public static void main(String[] args) { GenericTypeExample<Test> gte = null; SubGenericTypeExample sgte = new SubGenericTypeExample(); gte = sgte; gte.setField1(new Test());}
D:\>javap -verbose SubGenericTypeExampleCompiled from "SubGenericTypeExample.java"public class SubGenericTypeExample extends GenericTypeExample Signature: length = 0x2 00 11 SourceFile: "SubGenericTypeExample.java" minor version: 0 major version: 50 Constant pool:const #1 = Method #8.#20; // GenericTypeExample."<init>":()Vconst #2 = Field #21.#22; // java/lang/System.out:Ljava/io/PrintStream;const #3 = String #23; // sub class methodconst #4 = Method #24.#25; // java/io/PrintStream.println:(Ljava/lang/String;)Vconst #5 = class #26; // Testconst #6 = Method #7.#27; // SubGenericTypeExample.setField1:(LTest;)Vconst #7 = class #28; // SubGenericTypeExampleconst #8 = class #29; // GenericTypeExampleconst #9 = Asciz <init>;const #10 = Asciz ()V;const #11 = Asciz Code;const #12 = Asciz LineNumberTable;const #13 = Asciz setField1;const #14 = Asciz (LTest;)V;const #15 = Asciz (Ljava/lang/Object;)V;const #16 = Asciz Signature;const #17 = Asciz LGenericTypeExample<LTest;>;;const #18 = Asciz SourceFile;const #19 = Asciz SubGenericTypeExample.java;const #20 = NameAndType #9:#10;// "<init>":()Vconst #21 = class #30; // java/lang/Systemconst #22 = NameAndType #31:#32;// out:Ljava/io/PrintStream;const #23 = Asciz sub class method;const #24 = class #33; // java/io/PrintStreamconst #25 = NameAndType #34:#35;// println:(Ljava/lang/String;)Vconst #26 = Asciz Test;const #27 = NameAndType #13:#14;// setField1:(LTest;)Vconst #28 = Asciz SubGenericTypeExample;const #29 = Asciz GenericTypeExample;const #30 = Asciz java/lang/System;const #31 = Asciz out;const #32 = Asciz Ljava/io/PrintStream;;const #33 = Asciz java/io/PrintStream;const #34 = Asciz println;const #35 = Asciz (Ljava/lang/String;)V;{public SubGenericTypeExample(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; //Method GenericTypeExample."<init>":()V 4: return LineNumberTable: line 2: 0public void setField1(Test); Code: Stack=2, Locals=2, Args_size=2 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String sub class method 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 6: 0 line 7: 8public void setField1(java.lang.Object); Code: Stack=2, Locals=2, Args_size=2 0: aload_0 1: aload_1 2: checkcast #5; //class Test 5: invokevirtual #6; //Method setField1:(LTest;)V 8: return LineNumberTable: line 2: 0}
通过上面的例子可以看出,由擦除引起的冲突编译器会通过生成桥方法来解决。让由类型擦除后通过父类得到的类型为Object(或限定类型)的方法调用子类覆写的方法来实现一致性,化解冲突。
- Java笔记(一)——泛型
- JAVA 笔记(一)——配置
- 理解Java语言——Java学习笔记(一)
- Java复习笔记(一)——Java的运行机制
- Java学习笔记(一)——Java介绍
- Java学习笔记—Java I/O系统(一)
- JAVA笔记(一)
- Java 笔记(一)
- java笔记(一)
- JAVA 笔记(一)
- JAVA笔记(一)
- JAVA 笔记(一)
- JAVA 笔记(一)
- java笔记(一)
- java笔记 (一)
- JAVA笔记(一)
- java笔记(一)
- java学习笔记(一)泛型
- WebView·开车指南
- Linux C进程、线程
- Spring3.1.0实现原理分析(十三).MVC请求映射信息RequestMappingInfo详解
- Oracle那些事(6)-Net Manager使用
- 27. Remove Element
- Java笔记(一)——泛型
- Visual Studio各版本下载+有效密钥激活
- 常见的代理服务器
- SceneKit 太阳系小demo
- java 学习 第四天 核心类(数组)
- Hibernate实践异常记录self
- 微信小程序新增“星标”功能
- Mysql的命令语句
- 第一章 酒馆瘸子