泛型
1、泛型含义
泛型是一种类型参数,专门用来保存类型用的。
例如ArrayList< E>,这个E就是所谓的泛型了。使用ArrayList时,只要给E指定某一个类型,里面所有用到泛型的地方都会被指定对应的类型。
2、使用泛型的好处
不用泛型带来的问题
集合若不指定泛型,默认就是Object。存储的元素类型自动提升为Object类型,获取元素时得到的都是Object,若要调用特有的方法需要转型,这样会带来很多麻烦。
使用泛型带来的好处
可以在编译时对类型做出判断,避免不必要的类型转换操作,精简代码,也避免了因为类型转换导致的错误。
注意:泛型在代码运行时,泛型会被擦除,我们可以实现在代码运行过程中添加其他类型的数据到集合中,所以泛型是使用在编写时期的技术方式。
3、泛型的使用
3.1、泛型类
-
定义格式
在类型名后面加上一对尖括号,里面定义泛型。一般使用一个英文大写字母,如果有多个泛型使用逗号隔开。
public class 类名<泛型>{ //类型内部,可以把泛型当作是某一种类型使用 }
-
泛型的确定
当我们不确定类中的某个属性使用什么类型时,可以用泛型表示
public class 类名<TG>{ private TG 变量; }
在我们创建泛型类对象时,明确泛型类型
泛型类<Double> 对象 = new 泛型类<Double>(); //此时泛型类中的成员变量是Double类型
-
代码示例
//当不确定类中成员变量使用什么类型时可以用泛型来表示
class TgClass<T>{
private T demo;
public T getDemo() {
return demo;
}
public void setDemo(T demo) {
this.demo = demo;
}
}
public class Demo1 {
public static void main(String[] args) {
//创建对象,指定泛型类型为Double
TgClass<Double> d = new TgClass<Double>();
d.setDemo(2319848.89901);
System.out.println(d.getDemo());
}
}
3.2、泛型接口
-
定义格式
在接口名后面加上一对尖括号,里面定义泛型。一般使用一个英文大写字母,如果有多个泛型使用逗号隔开。
public interface 接口名<T>{ }
-
泛型确定
当定义接口时,其内部方法中的参数或者返回值类型不确定时,就可以使用泛型代替了。
比如这样
public interface 泛型接口<T>{ public void demo(T x); }
可以在实现类,实现接口时,确定接口中的泛型的类型(情况一),如果实现类和接口不指定具体类型,继续使用泛型指定含有泛型的类使用(情况二)。
比如这样
//情况一 public class 子类 implements 泛型接口<String>{ public void demo(String x){ } } //情况二 public class 子类<T> implements 泛型接口<T>{ //方法重写 public void demo(T a){ } } 子类<String> 对象 = new 子类<>();//创建子类对象时明确泛型的类型
-
代码示例
定义父接口
public interface Father<T> {
public void play(T x);
}
情况一
public class SonImp1 implements Father<Integer>{
@Override
public void play(Integer x) {
System.out.println(x + "参数类型是Integer");
}
}
public class Test1 {
public static void main(String[] args) {
SonImp1 sonImp1 = new SonImp1();
sonImp1.play(999);
}
}
情况二
public class SonImp2<T> implements Father<T>{
@Override
public void play(T x) {
System.out.println(x + "参数类型是String");
}
}
public class test2 {
public static void main(String[] args) {
SonImp2<String> sonImp2 = new SonImp2<>();
sonImp2.play("哈哈哈");
}
}
3.3、泛型方法
-
语法格式
修饰符号 <泛型> 返回值类型 方法名(泛型 参数1, 泛型 参数2,...){ //方法体 }
-
泛型确定
如果当前类没有声明泛型类,但该方法中的方法参数或者方法返回值不确定时,可以使用泛型类
public <T> void demo(T x, Ty){ }
调用含有泛型的方法时,传入的数据其类型就是泛型的类型
4、泛型通配符
4.1、泛型通配符介绍
当我们对泛型的类型确定不了了,而想要表达的是任意类型,可以使用泛型通配符给定。符号就是一个问号“?”,表示任意类型,用来给泛型指定的一种通配符。
4.2、泛型统配符使用
泛型统配符搭配集合使用一般在方法的参数中比较常见。
我们可以来个示例:
假如有一个Person父类
public class Person {
public int age;
public String name;
}
Person有两个子类Student和Teacher,这时候我们需要创建一个集合,向这个集合中添加指定元素类型,那么我们可以这样做
ArrayList<Student> array = new ArrayList<>();
ArrayList<Teacher> array = new ArrayList<>();
那还有什么方式向集合中添加指定元素类型呢?这时候,有人可能会联想到多态,那么我们是不是还可以这样
ArrayList<Person> array = new ArrayList<Student>();
很遗憾,java中是不允许这样做的,java中的泛型没有多态这个概念,正确的做法是这样
//使用通配符
ArrayList<?> array = new ArrayList<Student>();
这样做有什么好处嘞?我们试想,现在有两个方法如下
public static void demoStu(ArrayList<Student> array){
}
public static void demoStu(ArrayList<Teacher> array){
}
假如两个方法中的操作相同,只是传递参数的类型有差别,这时候上面的代码就显得有些冗余了,那么这时候,统配符就可以解决此问题
public static void demoStu(ArrayList<?> array){
}
这样做之后,无论我们传递的参数是ArrayList< Student>类型,还是ArrayList< Teacher>类型,方法都能接收参数,不会发生报错。
5、受限泛型
受限泛型是指,在使用通配符的过程中,对泛型做了约束,给泛型指定类型时,只能是某个类型父类型或者子类型。
5.1、泛型的下限
只能是某一类型,及其父类型,其他类型不支持。
<? super 类型>
<? super 最小类型>
//?可以是最小类型也可以是父类型
5.2、泛型的上限
只能是某一类型,及其子类型,其他类型不支持。
<? extends 类型>
<? extends 最大类型>
//?可以是最大类型,也可以是子类型
5.3、代码示例
4.2中的示例方法我们现在还能这样写
public static void demo(ArrayList<? extends Person> array){
}