java的泛型只在程序源码中存在,在编译成.class文件后就被替换成原生类型了,并且在相应的地方插入了强制类型转换代码,因为对于运行期的java语言来说ArraList<Integer>与ArrayList<String>就是同一个类,所以泛型技术实际上java语言的一颗语法糖,java语言中的泛型实现方法成为类型擦除,伪泛型。
包装类的“==”运算,再不遇到算术运算的情况下不会自动拆箱
Integer a = 1; Integer b = 2; Integer c = 3; Integer d = 3; Integer e = 321; Integer f = 321; Long g=3L; System.out.println(c==d);//true System.out.println(e==f);//false System.out.println(c==(a+b));//true System.out.println(g.equals(a+b));//false System.out.println(g==(a+b));//true
在JavaSE 5 之前定义一个容器类
public class Test { private Object a; public Test(Object a){ this.a =a; } public void set(Object a){ this.a =a; } public Object get(){ return a ;} public static void main(String[] args) { Test test = new Test(new Date()); Date date1 = (Date)test.get(); //需要强转 test.set("abc"); //可以插入任何类型的类对象 Date date2 = (Date)test.get(); //ClassCastException } }
一般情况下,我们使用容器只会存储一种类型的对象,泛型的主要目的之一就是用来指定容器要持有什么类型的对象,而且由编译器来保证类型的正确性。
下面是一个泛型类,在调用set方法时编译器会检查传入参数类型是否为Data,get()的时候也不需要强制类型转换,编译器就知道返值类型为Date
public class Test<T> { private T a; public Test(T a){ this.a =a; } public void set(T a){ this.a =a; } public T get(){ return a ;} public static void main(String[] args) { Test<Date> test = new Test<Date>(new Date()); Date date1 = test.get(); test.set("abc"); //编译不通过 } }
泛型方法:
是否拥有泛型方法,与其所在的类是否是泛型没有任何关系。如果使用泛型方法能取代整个类泛型化,那么就应该只使用泛型方法,方法的类型变量放在返回值前面。
当使用泛型类时,必须在创建对象的时候指定类型参数的值。
Test<Date> test = new Test<Date>(new Date());
而使用泛型方法时通常不必指明参数类型,因为编译器会给我们找到具体的类型(类型推断)。
public class Test { public static <T> List<T> makeList(T... ts) { List<T> list = new ArrayList<T>(); for (T t : ts) { list.add(t); } return list; } public static void main(String[] args) { List<String> list1 = makeList("a b c d".split(" ")); List<Integer> list2 = Test.<Integer> makeList(1, 2, 3, 4);// 指定参数类型 } }
上面的代码中,你无法获得任何泛型参数类型(T)的信息,你可以知道诸如类型参数标识符和泛型类型边界这样的信息,但是你无法知道用来创建某个特定实例的类型参数。你无法调用参数T的任何方法。。
Java 的泛型是使用擦除来实现的,这就意味着你在使用泛型时,泛型的类型参数将被擦除到他的第一个边界(它可能会有多个边界)。在没有限定边界的情况下。默认用Object作为参数类型的第一个边界,上面的泛型将T擦除后。
//类 擦除类型变量后 private Object a; public Test(Object a){ this.a =a; } public void set(Object a){ this.a =a; } public Object get(){ return a ;} //方法 擦除类型变量后 public static List makeList(Object... ts) { List list = new ArrayList(); for (Object t : ts) { list.add(t); } return list; }
擦除是java实现泛型的一种折中,因为泛型不是java语言出现就有的组成部分,如果泛型在Java1.0中就已经存在了,那么这个特性不会使用擦除来实现,它将使用具体化,使类型参数保持为第一实体。因此你就能在类型参数上执行基于类型的语言操作和反射操作。例如:上面的泛型方法,你可以直接像下面这样操作,代码编译时会自动监测你的类型参数T是否有replaceAll()方法。
public static <T> List<T> makeList(T... ts) { List<T> list = new ArrayList<T>(); for (T t : ts) { t.replaceAll("a", "");//Java中编译无法通过 list.add(t); } return list; }
Java中要想实现上面的功能就要把 参数类型改成<T extends String>
public static <T extends String> List<T> makeList(T... ts) { List<T> list = new ArrayList<T>(); for (T t : ts) { t.replaceAll("a", "");//ok list.add(t); } return list; }
泛型类型只有在静态类型检查期间才出现,在此之后,程序中的所有泛型类型都将被擦除,替换为它们的非泛型上界。例如,诸如List<T>这样的类型注解将被擦除为List,而普通的类型变量在未指定边界的情况下将被擦除为Object。
擦除的主要正当理由是从非泛化代码到泛化代码的转变过程,以及在不破坏现有类库的情况下,将泛型融入java语言。
另外,擦除和迁移兼容性意味着,使用泛型并不是强制的
List list = new ArrayList();
当你希望将类型参数不要仅仅当作Object处理时,就要付出额外的努力来管理边界。
上面泛型代码中,编译器负责set()的类型检查,然后转型Object插入set中,get()的时候负责将返回值进行转型。边界就是发生这些动作的地方。
Java泛型重用了extends 关键字。格式:T extends ***** 下面就可以根据自己定义的边界类型来调用方法。
public class Test { // T 必须是实现两个接口的子类,<T extends Date> 代表必须是Date的子类 public static <T extends Comparable<T> & CharSequence> List<T> makeList(T to, T... ts) { List<T> list = new ArrayList<T>(); for (T t : ts) { list.add(t); // T 可以使用上面两个接口中的方法 System.err.println(t.compareTo(to) > 0 ? t.length() : t.charAt(0)); } return list; } public static void main(String[] args) { makeList("c", "abcd", "efg"); // makeList(1,2); 编译无法通过 因为Integer没有实现 CharSequence 接口 } }
输出:
97
3
通配符“?”
通配符是用在泛型里的(List<? extends Number> List必须是个泛型List<E>),通配符跟类型变量限定十分相似? extends *****,但是还有一个附加的能力,即可以指定一个超类的类型限定, ? super *****,一般带有超类型限定的通配符可以向泛型对象写入,带子类型限定的通配符可以从对象独取对象。
public class Test { // 不能对nums进行赋值,因为不确定是Number的什么子类型 public static List<Double> aaa(List<? extends Number> nums) { List<Double> dou = new ArrayList<Double>(); for (Number num : nums) { dou.add(num.doubleValue()); } return dou; } public static void main(String[] args) { List<Integer> list1 = Arrays.asList(1, 2, 3); for (Double num : aaa(list1)) { System.err.println(num.toString()); } } }
输出
1.0
2.0
3.0
public class Test { //不能对nums 取值,因为不确定是Number的什么父类 public static void aaa(List<? super Number> nums) { List<Integer> inters = Arrays.asList(1, 2); for (Integer inter : inters) { nums.add(inter); } Collections.addAll(nums, 1.1, 2.2); } public static void main(String[] args) { List<Number> nums = new ArrayList<Number>(); aaa(nums); for (Number num : nums) { System.err.println(num); } } }
输出:
1
2
1.1
2.2
无界通配符“?”
比如List<?> ,表示我可以接收任何List<String>、List<Integer>等类型的泛型参数。
相关推荐
Java泛型编程指南.pdf 此文章译自SUN的泛型编程指南
Java Generics and Collections 英文版,详细描述java 泛型技术
java 泛型类的类型识别示例 java 泛型类的类型识别示例 java 泛型类的类型识别示例
java 泛型接口示例 java 泛型接口示例 java 泛型接口示例
java 泛型方法使用示例 java 泛型方法使用示例 java 泛型方法使用示例
主要介绍了Java泛型的用法及T.class的获取过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
这是一个使用JAVA实现的泛型编程,分为两部分,第一部分创建泛型类,并实例化泛型对象,得出相加结果。 第二部分用户自行输入0--4,选择要进行的加减乘除运算或退出,再输入要进行运算的两个数,并返回运算结果及...
java泛型技术之发展,学习JAVA 泛型的不错东东
1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1....
很好的Java泛型的总结,看完之后你一定会知道java泛型的底层机制,你一定会学会Java泛型!
4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip...
java,学习java泛型,java培训之泛型.pptxjava培训之泛型.pptxjava培训之泛型.pptxjava培训之泛型.pptx
深入理解java泛型,包括类名泛型的定义,方法泛型定义,泛型的返回
Sun公司的Java泛型编程文档,英文原版和网络翻译版,想对泛型有更清楚的认识的朋友可以看看,必定会有所帮助
java泛型详解.pdf
JAVA泛型源代码实现以下功能:返回数组元素的最大值/最小值下标;判断数组元素是否按升序排列;T对象数组排序;二分法查找key元素;
思维导图之Java泛型详解
Java泛型技术之发展
JAVA泛型教程(帮你解决学习泛型的苦恼). Java 泛型编程可能会碰到很多问题,本教程可能会对你有帮助哦。