Java基础-泛型
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
一.泛型的引入
由于集合可以存储任意类型的元素,导致取出时,如果出现强转就会引发运行时异常(ClassCastException)。怎么解决这个问题呢?使用集合时,必须明确集合中元素的类型。这种方式称为泛型。一旦我们给一个集合定义泛型,那就意味着这个集合只能传入指定的数据类型。
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.note; 8 9 import java.util.ArrayList; 10 import java.util.Collection; 11 import java.util.Iterator; 12 13 public class GenericDemo { 14 public static void main(String[] args) { 15 Collection coll = new ArrayList(); 16 coll.add("yinzhengjie"); 17 coll.add("尹正杰"); 18 coll.add("Java"); 19 coll.add(2018); 20 21 Iterator it = coll.iterator(); 22 while(it.hasNext()){ 23 Object obj = it.next(); 24 //在进行强转操作时,需要对数据进行判断,判断传入的数据是否为String类型,如果是则进行向下转型操作! 25 if(obj instanceof String) { 26 String str = (String)obj; 27 System.out.println(str.toUpperCase()); 28 } 29 } 30 } 31 } 32 33 34 /* 35 以上代码执行结果如下: 36 YINZHENGJIE 37 尹正杰 38 JAVA 39 */
JDK1.5出现新的安全机制,保证程序的安全性,就是我们今天要说明的泛型,它指明了集合中存储数据的类型。
二.泛型的定义与使用
看了上面的代码,你可以明显的发现可以往一个结合里面塞各种数据类型,但是在遍历该集合的每一个元素时,你会清楚的知道,需要对不同的数据类型做相应的向下转型,这样才能使用元素本身的方法。这样就给程序员带了很多麻烦,需要对引用数据类型做断言操作,如果在定义集合时就规定好传入的数据类型就可以避免断言操作啦!我们可以对上面的代码用泛型对代码进行改进如下:
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.note; 8 9 import java.util.ArrayList; 10 import java.util.Collection; 11 import java.util.Iterator; 12 13 public class GenericDemo { 14 public static void main(String[] args) { 15 //后面的ArrayList是可以不写数据类型的,默认与前面的相同,我们称为菱形语法! 16 Collection<String> coll = new ArrayList<>(); 17 coll.add("yinzhengjie"); 18 coll.add("尹正杰"); 19 coll.add("Java"); 20 // coll.add(2018); #这里的基本数据类型会自动装箱为Integer类型,很显然和String不是一种类型,无法添加到该集合 21 22 //创建迭代器的时候也会传入相应的泛型。 23 Iterator<String> it = coll.iterator(); 24 while(it.hasNext()) { 25 //此处也不需要做向下转型,直接就可以调用String的方法啦! 26 System.out.println(it.next().toUpperCase()); 27 } 28 } 29 } 30 31 32 /* 33 以上代码执行结果如下: 34 YINZHENGJIE 35 尹正杰 36 JAVA 37 */
综上所述,我们可以归纳泛型有两大好处:
1>.将运行时期的ClassCastException转移到了编译时期变成了编译失败。
2>.避免了类型转换的麻烦。
三.Java中的伪泛型
java中的泛型是伪泛型(也就是假的泛型)。Java中的泛型只是一种编译手段。比如:“ArrayList<Integer> arr = new ArrayList<>();”很明显就是泛型,当然你调用"arr.add(Integer number)"方法往arr集合中添加元素时,若该元素不是Integer类型时就会编译失败,若该元素是Integer类型,则会编译成功。但是编译后的字节码文件(*.class)中是没有泛型的。
Java中泛型只是在编译时候才会体现,一旦编译成功后的字节码文件中压根就没有泛型的踪影,这就是Java的伪泛型。那么问题来了,既然Java运行时并没有泛型那还能保证数据的安全性吗?答案是肯定的,因为在编译的时候,不符合的数据类型是不会让代码编译通过的。所以Java靠编译手段完全是可以保证安全性。Java反编译字节码文件后你会发现源代码是没有泛型的哟!
四.自定义泛型类
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.note; 8 9 import java.util.Date; 10 11 //定义泛型类 12 class Student<T>{ 13 T score;//String score;Integer score; 14 //泛型方法 15 public void setScore(T t){ 16 score = t; 17 } 18 public T getScore(){ 19 return score; 20 } 21 } 22 23 public class GenericDemo{ 24 public static void main(String[] args) { 25 26 Student<Integer> s1 = new Student<Integer>(); //指定泛型为Integer类型 27 s1.setScore(10); //传递参数的时候就必须为Integer类型 28 System.out.println(s1.getScore()); 29 30 Student<String> s2 = new Student<>(); //指定泛型为String类 31 s2.setScore("优秀"); //传参数的时候就必须为String类型 32 System.out.println(s2.getScore()); 33 } 34 35 } 36 37 38 /* 39 以上代码执行结果如下: 40 10 41 优秀 42 */
五.自定义泛型方法
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.note; 8 9 import java.util.Date; 10 11 12 13 import java.util.Date; 14 15 //定义泛型方法. 16 class Generic1{ 17 //泛型方法 18 public <T>void show(T t){//相当于Object 19 System.out.println(t); 20 } 21 } 22 23 public class GenericDemo{ 24 25 public static void main(String[] args) { 26 27 Generic1 g1 = new Generic1(); 28 29 g1.show("yinzhengjie"); //存储String类型 30 g1.show(2018); //存储Integer类型 31 g1.show(new Date()); //存储Date类型 32 33 } 34 } 35 36 37 /* 38 以上代码执行结果如下: 39 yinzhengjie 40 2018 41 Wed Apr 25 13:25:45 GMT+08:00 2018 42 */
六.自定义泛型接口
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.note; 8 9 //定义泛型接口 10 interface InterA<T>{ 11 public abstract void show(T t); 12 } 13 14 //接口实现类 15 //实现类知道接口中的具体类型,此时实现类可以是非泛型类 16 class A implements InterA<String>{ 17 18 @Override 19 public void show(String t) { 20 System.out.println(t); 21 } 22 23 } 24 25 //实现类在实现接口时,本身还是一个泛型类 26 class B<T> implements InterA<Integer>{ 27 @Override 28 public void show(Integer t) { 29 System.out.println(t); 30 } 31 32 public void show2(T t) { 33 System.out.println(t); 34 } 35 36 } 37 38 //实现类也不知道接口中泛型的具体类型,那么子类也是一个泛型类 39 class C<T> implements InterA<T>{ 40 41 @Override 42 public void show(T t) { 43 System.out.println(t); 44 } 45 } 46 47 public class GenericDemo{ 48 public static void main(String[] args) { 49 A a = new A(); 50 a.show("yinzhengjie"); 51 52 53 B<String> b = new B<>(); 54 b.show(2018); 55 b.show2("尹正杰"); 56 57 C<Integer> c = new C<>(); 58 c.show(5200); 59 } 60 } 61 62 63 64 /* 65 以上代码执行结果如下: 66 yinzhengjie 67 2018 68 尹正杰 69 5200 70 */
七.泛型的通配符
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.note; 8 9 import java.util.ArrayList; 10 import java.util.Collection; 11 import java.util.HashSet; 12 import java.util.Iterator; 13 14 public class GenericDemo{ 15 public static void main(String[] args) { 16 ArrayList<String> array = new ArrayList<String>(); 17 HashSet<Integer> set = new HashSet<Integer>(); 18 array.add("yinzhengjie"); 19 array.add("尹正杰"); 20 21 set.add(2018); 22 set.add(328); 23 24 iterator(array); 25 iterator(set); 26 27 } 28 29 //定义方法,可以同时迭代2个集合,使用泛型的通配符"?" 30 public static void iterator(Collection<?> coll) { 31 //泛型通配符“?”可以匹配所有的数据类型。 32 Iterator<?> it = coll.iterator(); 33 while(it.hasNext()) { 34 //it.next())获取的对象应该是Object类型,此处我们最好不要强制转型,因为我们不知道传入的迭代对象是set还是array! 35 System.out.println(it.next()); 36 } 37 } 38 39 40 } 41 42 43 44 /* 45 以上代码执行结果如下: 46 yinzhengjie 47 尹正杰 48 2018 49 328 50 */
八.泛型的限定
1>.* ? extends Animal : 上限限定,限制的是父类,即Animal和它的子类可以使用.
2>.* ? super Animal: 下限限定,限制的是子类,即Animal和它的父类都可以使用.
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.note; 8 9 10 import java.util.Collection; 11 12 /* 13 * 泛型类是没有多态性 14 * 泛型如何实现类似于多态性? 15 * 通配符能实现 16 * ? 17 * ? extends Animal : 上限限定,Animal和它的子类可以使用 18 * ? super Animal: 下限限定,Animal和它的父类都可以使用 19 * 20 * 类型限定的使用 21 * 22 */ 23 class Animal { 24 25 } 26 27 class Dog extends Animal { 28 29 } 30 31 class Cat extends Animal { 32 33 } 34 35 class Demo<T> { 36 37 } 38 39 class DemoTest { 40 public void test6(Collection<? extends Animal> c){// 41 //由于不知道具体的类型,所以不能放东西 42 // c.add(null); 43 //由于元素的类型要么是Animal,要么是它的子类,都可以使用Animal去接 44 for (Animal animal : c) { 45 46 } 47 } 48 public void test7(Collection<? super Animal> c){// 49 //由于不知道具体的类型,但是我知道都是Animal或者是它的父类,Animal和它的子类可以自动转成Animal 50 c.add(new Animal()); 51 c.add(new Dog()); 52 c.add(new Cat()); 53 // c.add(new Object()); 54 //由于不知道类型的上限,所以不能往外拿元素,Object是所有类的鼻祖,此处我们需要忽略它。 55 // for (Object object : c) { 56 // 57 // } 58 } 59 60 public void test(Demo<Animal> d) {// 多态?Demo<Dog> 61 62 } 63 public void test2(Demo<?> d) {// 相当于Object,但是如果你写Object的话,那么传入的对象就只能是Object啦! 64 65 } 66 public void test3(Demo<Object> d) {// 相当于Object,调用者此处传入的必须是Object类,不能是其子类。 67 68 } 69 70 public void test4(Demo<? extends Animal> d){//Animal或者它的子类 71 72 } 73 public void test5(Demo<? super Animal> d){//Animal或者它的父类都可以使用 74 75 } 76 } 77 78 public class GenericDemo1 { 79 80 public static void main(String[] args) { 81 DemoTest dt = new DemoTest(); 82 83 84 dt.test5(new Demo<Animal>()); 85 dt.test5(new Demo<Object>()); 86 // dt.test5(new Demo<Dog>());//NG 87 88 // dt.test4(new Demo<Animal>()); 89 // dt.test4(new Demo<Dog>()); 90 // dt.test4(new Demo<Cat>()); 91 // dt.test4(new Demo<Object>());//NG 92 93 94 95 // dt.test2(new Demo<Integer>()); 96 97 // Demo<Dog> d = new Demo<Dog>(); 98 // Demo<Animal> d2 = new Demo<>(); 99 100 // dt.test(d2);//Demo<Animal> Demo<Dog> 101 102 } 103 104 }
案例展示:
将酒店员工,厨师,服务员,经理,分别存储到3个集合中,定义方法可以同时遍历这3个集合,遍历三个集合的同时,可以调用工作方法。
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.note; 8 9 public abstract class Employee { 10 public abstract void Work(); 11 }
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.note; 8 9 public class Waiter extends Employee { 10 private String name; 11 private String department; 12 public Waiter() { 13 super(); 14 } 15 public Waiter(String name, String department) { 16 super(); 17 this.name = name; 18 this.department = department; 19 } 20 public String getName() { 21 return name; 22 } 23 public void setName(String name) { 24 this.name = name; 25 } 26 public String getDepartment() { 27 return department; 28 } 29 public void setDepartment(String department) { 30 this.department = department; 31 } 32 @Override 33 public void Work() { 34 System.out.println(this.getName()+" 正在工作中......"); 35 } 36 37 38 39 }
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.note; 8 9 public class Cook extends Employee { 10 private String name; 11 private String department; 12 public String getName() { 13 return name; 14 } 15 public void setName(String name) { 16 this.name = name; 17 } 18 public String getDepartment() { 19 return department; 20 } 21 public void setDepartment(String department) { 22 this.department = department; 23 } 24 public Cook() { 25 super(); 26 } 27 public Cook(String name, String department) { 28 super(); 29 this.name = name; 30 this.department = department; 31 } 32 @Override 33 public void Work() { 34 System.out.println(this.getName()+" 正在工作中......"); 35 36 } 37 38 }
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.note; 8 9 public class LobbyManager extends Employee { 10 private String name; 11 private String department; 12 private double bonus; 13 public LobbyManager() { 14 super(); 15 } 16 public LobbyManager(String name, String department, double bonus) { 17 super(); 18 this.name = name; 19 this.department = department; 20 this.bonus = bonus; 21 } 22 public String getName() { 23 return name; 24 } 25 public void setName(String name) { 26 this.name = name; 27 } 28 public String getDepartment() { 29 return department; 30 } 31 public void setDepartment(String department) { 32 this.department = department; 33 } 34 public double getBonus() { 35 return bonus; 36 } 37 public void setBonus(double bonus) { 38 this.bonus = bonus; 39 } 40 @Override 41 public void Work() { 42 System.out.println(this.getName()+" 正在工作中......"); 43 } 44 45 }
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.note; 8 9 import java.util.ArrayList; 10 import java.util.Iterator; 11 12 public class GenericDemo { 13 public static void main(String[] args) { 14 //创建三个集合对象 15 ArrayList<Cook> cook = new ArrayList<>(); 16 ArrayList<Waiter> waiter = new ArrayList<>(); 17 ArrayList<LobbyManager> manager = new ArrayList<>(); 18 19 //每个集合都存储自己的元素 20 cook.add(new Cook("邓西西","主厨001")); 21 cook.add(new Cook("方合意","主厨002")); 22 23 waiter.add(new Waiter("陶涛","服务部门001")); 24 waiter.add(new Waiter("邓西","服务部门002")); 25 26 manager.add(new LobbyManager("桂阳","董事会",12463125247.28)); 27 manager.add(new LobbyManager("李洋","董事会",12463125247.28)); 28 iterator(cook); 29 iterator(waiter); 30 iterator(manager); 31 32 } 33 //上限限定,可以传递Employee以及传递他的子类对象。 34 public static void iterator(ArrayList<? extends Employee> array) { 35 Iterator<? extends Employee> it = array.iterator(); 36 while(it.hasNext()) { 37 //获取出来的it.next()的数据类型是Employee,因为我们并不知道它传入的是什么类型的子类。 38 Employee e = it.next(); 39 e.Work(); 40 } 41 } 42 } 43 44 45 46 /* 47 以上代码执行结果如下: 48 邓西西 正在工作中...... 49 方合意 正在工作中...... 50 陶涛 正在工作中...... 51 邓西 正在工作中...... 52 桂阳 正在工作中...... 53 李洋 正在工作中...... 54 */