泛型(参数化类型)
------------恢复内容开始------------
一、泛型的概念
泛型:即参数化类型,那什么又是参数化类型呢?以前我定义一个属性或者方法的时候,我们都会明确具体的类型,比如int、String、void等等,但是参数化之后,就不明确类型,只有在具体调用对象的时候,才传递实际类型实参,这就叫参数化类型,把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型,简而言之,就是在定义一个对象的时候没有赋予确切的参数类型,在实例化一个对象的时候传递确切的参数,类似于形参和实参的一种情况。
Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常.
Java泛型的设计目的:主要是为了解决当向一个集合输入不同的数据类型时,输出读取时会报错的问题,例如向一个集合中添加字符串、数字、字符等类型的数据时,当输出时会报错误。
相关术语:
ArrayList<E>
中的E称为类型参数变量,常用的类型参数符号有K、E、V、MArrayList<Integer>
中的Integer称为实际类型参数- 整个称为
ArrayList<E>
泛型类型 - 整个
ArrayList<Integer>
称为参数化的类型ParameterizedType - 一些常用的泛型类型变量:
E:元素(Element),多用于java集合框架
K:关键字(Key)
N:数字(Number)
T:类型(Type)
V:值(Value)
二、为什么需要泛型
早期Java是使用Object来代表任意类型的,但是向下转型有强转的问题,这样程序就不太安全
首先,我们来试想一下:没有泛型,集合会怎么样
- 把对象扔进集合中,集合是不知道元素的类型是什么的,仅仅知道是Object。因此在get()的时候,返回的是Object。外边获取该对象,还需要强制转换
- Collection、Map集合对元素的类型是没有任何限制的。本来我的Collection集合装载的是全部的Dog对象,但是外边把Cat对象存储到集合中,是没有任何语法错误的。
package daily; import java.util.LinkedList; import java.util.List; public class fanxing { public static void main(String[] args) { List li = new LinkedList(); li.add("java编程思想");//向List集合中加入字符串 li.add('爱');//向List集合中加入一个字符 li.add(97);//向List集合中加入整型数据 for (int i=0;i<li.size();i++) { String j = (String) li.get(i);//当然你这里不用强制类型转换的话,也是能完全打印输出的,因为它默认为时Object,但是你使用增强for的话,就无法明确具体的类型 System.out.println(j); } } } 运行结果: java编程思想 Exception in thread "main" java.lang.ClassCastException: class java.lang.Character cannot be cast to class java.lang.String //报错
2.1有了泛型后使用增强foreach遍历集合
//创建集合对象 ArrayList<String> list = new ArrayList<>(); //明确集合为String类型,则只能往该集合添加字符串元素 list.add("hello"); list.add("world"); list.add("java"); //遍历,由于明确了类型.我们可以增强for for (String s : list) { System.out.println(s); }
2.1有了泛型后能减少方法的重载
我们编写代码的时候,当遇到功能相似的方法的话,就会进行方法的重载,比如计算两个浮点型的和,计算两个整型的和,这两个方法的功能相似,就可以进行方法的重载
但是引入泛型之后,只需要写入一个方法就可以了,减少该方法的重载。
三、泛型分类
泛型的分类主要分为:泛型类、泛型接口、泛型方法
3.1 泛型类
泛型类就是把泛型定义在类上,用户使用该类的时候,才把类型明确下来....这样的话,用户明确了什么类型,该类就代表着什么类型...用户在使用的时候就不用担心强转的问题,运行时转换异常的问题了。
3.1.1类的定义:
/* 1:把泛型定义在类上 2:类型变量定义在类上,方法中也可以使用 */ public class ObjectTool<T> { private T obj;//变量的数据类型和泛型类所传递的数据类型一致 public T getObj() {//函数的返回类型和泛型类所传递的数据类型一致 return obj; } public void setObj(T obj) { this.obj = obj; } }
测试代码:
public static void main(String[] args) { //创建对象并指定元素类型为字符串类型 ObjectTool<String> tool = new ObjectTool<>(); tool.setObj("一只懒熊"); String s = tool.getObj(); System.out.println(s); //创建对象并指定元素类型为整数类型 ObjectTool<Integer> objectTool = new ObjectTool<>(); /* 如果我在这个对象里传入的是String类型的,它在编译时期就通过不了了. */ objectTool.setObj(10); int i = objectTool.getObj(); System.out.println(i); }
3.1.2泛型类的继承:泛型类是拥有泛型这个特性的类,它本质上还是一个Java类,那么它就可以被继承
那它是怎么被继承的呢?这里分两种情况
- 子类明确泛型类的类型参数变量
- 子类不明确泛型类的类型参数变量
情况1:子类明确泛型类的类型参数变量
//把泛型定义在接口上 public interface Inter<T> { public abstract void show(T t); } //定义一个子类来实现接口 public class InterImpl implements Inter<String> {//子类明确泛型类的类型参数变量为String: @Override public void show(String s) { System.out.println(s); } } //测试代码 public class mingquefanxingcanshu { public static void main(String[] args) { Inter<String> i = new InterImpl(); i.show("hello java!"); } }
情况2:子类不明确泛型类的类型参数变量
//把泛型定义在接口上 public interface Inter<T> { public abstract void show(T t); } //定义一个子类来实现接口 public class InterImpl<T> implements Inter<T> {//实现类不明确泛型类的类型参数变量,实现类也要定义出<T>类型的 @Override public void show(T s) {//子类方法也要继承类的数据类型T System.out.println(s); } } //测试代码 public class mingquefanxingcanshu { public static void main(String[] args) { Inter<String> i = new InterImpl(); i.show("hello java!"); } }
注意:
- 实现类的要是重写父类的方法,返回值的类型是要和父类一样的!
- 类上声明的泛形只对非静态成员有效
------------恢复内容结束------------