泛型的引入
我们为什么需要泛型?
任何工具的产生都是离不开实际需求,凡是存在的必定是合理的,如果想知道泛型是什么,泛型这个概念为什么会出现,我们就让程序处于一个没有泛型的代码世界中,通过引入泛型前后程序的优劣的比较,从而明白泛型的作用和意义。
请在不使用泛型的情况下完成下列要求:
设计一个类,类里面有一个数组,这个数组可以存储任意的类型的值,根据成员方法getvalue() 和 setvalue() 获得和设置数组对应下标的值。
思路:
我们都知道Object类是所有类的父类,我们在多态的章节中学习过,子类可以进行向上转型到父类,那么如果父类是Object类,就可以进行所有类的存储了,换句话说,建立一个Object类的数组,用此数组进行不同类型的值的存储就可以了。废话不多说,直接上代码
class All_data_types{
Object[] objects = new Object[10];
public All_data_types(){}
public void setObjects(int pose, Object value) {
this.objects[pose] = value;
}
public Object getObjects(int pose) {
return objects[pose];
}
}
public class Main {
public static void main(String[] args) {
All_data_types all_data_types = new All_data_types();
all_data_types.setObjects(0, "hello");
all_data_types.setObjects(1,23);
all_data_types.setObjects(2,true);
System.out.println(all_data_types.getObjects(0));
System.out.println(all_data_types.getObjects(1));
System.out.println(all_data_types.getObjects(2));
String str = all_data_types.getObjects(0);//编译报错
}
}
由此可以看到程序的问题,首先就是不能用具体的变量类型来接收,因为子类可以转型为父类,但是父类不能转型为子类,这样的话就必须进行强制类型转换,这样的代码在交给客户端的时候极易出现错误。
并且我们做软件开发不可能这样写的,因为这样的维护成本及其的高,因为我们要输入的数据类型的不同,所以我们需要手动的一个一个的进行输入,会大大的增加程序员的工作量,而且需要改变数据类型的时候我们也需要一个一个的改变,因此,我们迫切的需要一种统一的,能对数据类型进行整合的一个新的工具
泛型
更多情况下,我们还是希望一个容器只能够持有一种数据类型。而不是同时持有这么多类型。所以,泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。
写一个泛型类 My_Gener
class My_Gener<T>{
public T[] my_gen =(T[]) new Object[10];//现阶段这样写,之后不要这样写
public void setMy_gen(int pose, T value){
this.my_gen[pose] = value;
}
public T getMy_gen(int pose){
return this.my_gen[pose];
}
}
public class Main {
public static void main(String[] args) {
My_Gener<Integer> my_gener = new My_Gener<>();
my_gener.setMy_gen(0, 15);
my_gener.setMy_gen(1, 23);
System.out.println(my_gener.getMy_gen(0));
System.out.println(my_gener.getMy_gen(1));
}
}
用泛型所存在的两个优点,第一个就是在存放元素的时候,不再会进行类型的检查,第二个就是在取出的时候会自动地帮我们进行类型的转换,也就是用进行类型的强制类型转换了,而这两布都是在编译的时候进行完成的,运行的时候是没有泛型的概念的,这种概念其实就是泛型中的擦除机制。
擦除机制
这里引入一个知乎上的文章:https://zhuanlan.zhihu.com/p/51452375
擦除机制可以理解为把T都擦除成为了Object,那么如果是这样,我们下面这段代码为什么会出错?
class My_Gener<T>{
public T[] my_gen =new T[10];
public void setMy_gen(int pose, T value){
this.my_gen[pose] = value;
}
public T getMy_gen(int pose){
return this.my_gen[pose];
}
public T[] getT_(){
return my_gen;
}
}
public class Main {
public static void main(String[] args) {
My_Gener<Integer> my_gener = new My_Gener<>();
Integer integer = my_gener.getT_();
}
}
在汇编中,在上述的setMy_gen方法中,传入的值是T类型和int类型的,但是在get方法中return的值返回的其实是一个Object类型的,在没学过汇编的同学中,大家可以简单理解为在运行的时候,T这一概念已经消失,反而被擦除成为了Object类就可以了
所以,上面的这一行代码,之所以是错误的,是因为被擦除成Object类之后,返回值也是Object,因此就不能用具体的类,比如Integer类来接收,就必须强制类型转换,否则就会报错。
因此把第一行代码改为:
public T[] my_gen =(T[])new Object[10];
手动添加一个强制类型的转换,就会编译通过。
泛型的上界
我们现在要实现一个可以用来比较的泛型类
找出元素中的最大值:
class Person implements Comparable<Person> {
public String name;
public int age;
public Person(){
this.name = "nobody";
this.age = 0;
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
return this.name.compareTo(o.name);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class Alg<T extends Comparable<T>>{
public T find_Max(T[] arrays){
T max = arrays[0];
for(int i = 0; i < arrays.length; i++){
if(max.compareTo(arrays[i]) < 0){
max = arrays[i];
}
}
return max;
}
}
public class Main {
public static void main(String[] args) {
Alg<Person> alg = new Alg<>();
Person[] peoples = {new Person("zhangsan", 13)
, new Person("lisi", 34)
, new Person("wangwu", 22)
, new Person("zhouermazi", 43)};
Person person = alg.find_Max(peoples);
System.out.println(person);
}
}
Alg中的上界就是泛型T继承了Comparable,因此如果Person想要用Alg中的find_Max方法就也需要继承Comparable方法进行重写。
泛型方法
静态泛型方法
静态泛型方法的定义:
class Alg{
public static<T extends Comparable<T>> T find_Max(T[] arrays){
T max = arrays[0];
for(int i = 0; i < arrays.length; i++){
if(max.compareTo(arrays[i]) < 0){
max = arrays[i];
}
}
return max;
}
}
public class Main {
public static void main(String[] args) {
Person[] peoples = {new Person("zhangsan", 13)
, new Person("lisi", 34)
, new Person("wangwu", 22)
, new Person("zhouermazi", 43)};
Person person = Alg.<Person>find_Max(peoples);
System.out.println(person);
}
}
这个Alg类就是普通类,而静态方法就是泛型方法,泛型方法中定义的泛型占位符和泛型类定义的占位符是不冲突的,泛型方法的占位符优先级最高。
泛型方法只需要把静态泛型方法的static去掉就可以了。