零、最直观的理解——泛型是什么
泛型 ,顾名思义就是 广泛的数据类型,也就是说什么数据类型都可以。
一般来说,我们见到的泛型就是这个样子,用 T 表示。
如下所示,在类名后方申明泛型 T,接着就可以在成员变量、方法中使用泛型了。
1. public class User<T> { 2. 3. private T name; 4. }
对于这个name变量,我们可以放入不同数据类型的值,比如字符串String,比如整数int......
下面测试类创建了三个User对象,第一个对象的name属性放入了String字符串,第二个放入了int整数,第三个放入了Double浮点数。
1. //泛型测试方法 2. public static void main(String[] args) { 3. User user1 = new User(); 4. user1.setName("zwz"); 5. System.out.println(user1.getName() instanceof String); 6. 7. User user2 = new User(); 8. user2.setName(123456); 9. System.out.println(user2.getName() instanceof Integer); 10. 11. User user3 = new User(); 12. user3.setName(123.456); 13. System.out.println(user3.getName() instanceof Double); 14. }
我们发现,不管放入什么数据类型,我们都可以正常运行,运行结果如下图所示
像上面这样,我们就可以理解为泛型的一种应用。
即如果数据类型不确定,可以使用泛型方法的方式,达到简化代码、提高代码重用性的目的。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数,使代码可以应用于多种数据类型。
以上就是我对泛型比较直观的理解,如果你想了解更多,请继续阅读下面内容。
内容比较枯燥,但还是很有用的。
一、举个栗子——从ArrayList说起
List<String> list = new ArrayList<String>();
这行代码,相信很多小伙伴都经常使用。在Java中,虽然后原生的数组String[],但是String[]功能并不能满足我们的需要,更多的任会选择使用Java的集合类——List。
List有两大实现,那就是是 ArrayList 和 LinkedList,分别代表顺序表和链表这两个数据结构。
虽然顺序表和链表各有各的好处,但是对于新手来说,总喜欢使用ArrayList ,相信这也是大家有目共睹的。
细心地小伙伴应该发现过,在Eclipse中,我们按下Ctrl键 + 鼠标左键,就可以进入某个类或者方法的具体实现。
就像这样,我们可以打开List的底层代码。
1. public interface List<E> extends Collection<E> { 2. /** 3. * Appends the specified element to the end of this list (optional 4. * operation). 5. * 6. * <p>Lists that support this operation may place limitations on what 7. * elements may be added to this list. In particular, some 8. * lists will refuse to add null elements, and others will impose 9. * restrictions on the type of elements that may be added. List 10. * classes should clearly specify in their documentation any restrictions 11. * on what elements may be added. 12. * 13. * @param e element to be appended to this list 14. * @return {@code true} (as specified by {@link Collection#add}) 15. * @throws UnsupportedOperationException if the {@code add} operation 16. * is not supported by this list 17. * @throws ClassCastException if the class of the specified element 18. * prevents it from being added to this list 19. * @throws NullPointerException if the specified element is null and this 20. * list does not permit null elements 21. * @throws IllegalArgumentException if some property of this element 22. * prevents it from being added to this list 23. */ 24. boolean add(E e); 25. 26. // 其余方法省略 27. }
在第一行类的定义代码中,就使用到了泛型。
public interface List<E> extends Collection<E>
也就是说,在我们平时使用List的时候,可以初始化为String类型的List
List<String> list = new ArrayList<String>();
也可以初始化为 Integer(int)的List
List<Integer> list = new ArrayList<Integer>();
当你的List 为String类型,在调用add()方法时,IDE会提示你是String类型的参数
同理,当List为Integer类型,调用add()方法,
在JDK1.5之前,List的add()方法的参数是Object类型,不管把什么对象放入List中,都会被强制转换为Object类型。
在通过get()方法取出集合元素时,必须进行强制类型转换,这样不仅繁琐,也容易出现强制转换异常。
从JDK1.5开始,引入了泛型这样一个新概念,改写了集合框架中的所有接口和类,增加了泛型的支持。
使用泛型集合在创建集合对象的时候,制定了集合中的元素类型,从集合中取出元素时,无需强制类型转换,并且在集合中放入非指定类型的对象,IDE将出现编译错误。
比如下图在String类型的集合中插入Double类型的浮点数:
使用泛型集合在创建集合对象时置顶集合中的元素类型,从集合中取出元素时无需进行强制类型转换。
二、更多应用——泛型用法远不止这些
在集合中使用泛型只是多种应用中的一种,在接口、类、方法等方面也有着泛型的广泛应用。
2.1 泛型类
对于一些常常处理不同类型数据转换的类,我们可以使用泛型来定义。比如一个人的身高,可以认为是整数,也可以是浮点数。
1. public class User<T> { 2. 3. private T height; 4. 5. public User(T height) { 6. this.height = height; 7. } 8. 9. public void say() { 10. System.out.println("I am " + this.height + "cm(s) tall."); 11. } 12. 13. public T getHeight() { 14. return height; 15. } 16. 17. public void setHeight(T height) { 18. this.height = height; 19. } 20. }
我们在测试方法中传入不同数据类型,都可以say()方法
1. public class Main { 2. public static void main(String[] args) { 3. User user1 = new User(180); 4. user1.say(); 5. 6. User user2 = new User(188.5); 7. user2.say(); 8. } 9. }
2.2 泛型接口
1. interface SayHeight<T>{ 2. public T getHeight(); 3. };
泛型接口同理,就是拥有一个或者多个类型参数的接口,泛型接口接口的定义方式和定义泛型类比较类似。
1. public class User<T> implements SayHeight{ 2. 3. private T height; 4. 5. public T getHeight() { 6. return height; 7. } 8. 9. public void setHeight(T height) { 10. this.height = height; 11. } 12. }
即getHeight()返回的类型由外部决定。
1. public class Main { 2. public static void main(String[] args) { 3. User<String> user = new User(); 4. user.setHeight("188.8"); 5. System.out.println(user.getHeight()); 6. } 7. }
2.3泛型方法
有一些方法常常需要对某个类型数据进行处理,但是处理的数据类型不唯一,就可以通过定义泛型方法,简化代码,以提高代码利用率。
1. public class Main { 2. 3. public <String> void showType(String s) { 4. System.out.println(s.getClass().getName()); 5. } 6. 7. public static void main(String[] args) { 8. Main main = new Main(); 9. main.showType(123); 10. main.showType(123.456); 11. main.showType(123.456f); 12. main.showType(123l); 13. main.showType("hello zwz"); 14. } 15. }
虽然在方法中写了String,但是可以传入多种数据类型的参数,进行不同的操作。