1.泛型
所谓泛型 其实就是一种类型参数(我们平常所见到的参数指的就是方法中的参数 他接收有外界传递来的值 然后在方法中进行使用) 并且还提高了代码的复用率
何以见得提高了代码的复用率 其实就是通过对比使用了泛型技术和没有使用泛型技术之间的区别:
以下是没有使用泛型技术的案例
public class Student1 {// 第一个学生类他的分数接收的是一个浮点类型的数据double score;// 定义getter、setter方法void setScore(double score){this.score = score;}double getScore(){return score;}
}
public class Student2 {// 这个学生类接收的是字符串类型的分数String score;// 定义getter、setter方法void setScore(String score){this.score = score;}String getScore(){return score;}
}
public class Student3 {// 第三个学生类的分数就定义为整型int score;// 定义相关的getter、setter方法void setScore(int score){this.score = score;}int getScore(){return score;}
}
从以上代码中我们可以看出 定义了三种不同类型的分数 就要定义三份逻辑相似的代码 如果分数的类型规模一旦庞大起来 那么其实效率是十分低下的
现在引入了泛型技术以后 就不会出现以上这种局面 并且会提高同一份代码的复用率
public class Student<T>{// 泛型是一种类型参数 有外界的实参所决定 并且可以在类中使用T score;// 定义一个getter、setter方法void setScore(T score){this.score = score;}T getScore(){return score;}
}
我们可以在主函数中测试一下这个代码的正确性
public class Main {public static void main(String[] args) {// 我们来试用一下泛型好不好用吧Student<String> s1 = new Student<>();Student<Integer> s2 = new Student<>();Student<Double> s3 = new Student<>();s1.setScore("优秀");s2.setScore(100);s3.setScore(99.5);System.out.println(s1.getScore());System.out.println(s2.getScore());System.out.println(s3.getScore());}}
从结果可以看出 这个泛型类型是写的很成功的 并且从中我们还能够得知 从java7开始 右边<>内就可以不用写出具体的泛型了
而且我们的泛型名称是有命名建议的:
T Type
E Element
K Key
N Number
V Value
S、U、V 当你想要表示连续类型中的第二个、第三个、第四个泛型的时候的命名建议
类名后面不仅可以支持一个泛型 也可以支持多个泛型
public class Student<N, S>{private N no;private S score;public Student(N no, S score){this.no = no;this.score = score;}
}
public class Main {public static void main(String[] args) {Student<Integer, Double> s = new Student<>(1, 100.0);}
}
何为泛型类型?其实就指的是哪些使用了泛型的类或者接口
比如一些诸如java.util.Comparator、java.util.Comparable之类的接口
2.泛型类型的继承
我们从前面的多态都知道 如果类或者接口之间存在父子关系的话 那么让父类引用指向子类对象是可以编译成功的
以下的案例中 从结果可以明显发现strBox和intBox不存在父子关系 objBox和strBox之间不存在父子关系
有些人可能会以泛型的继承关系来作为泛型类型的继承关系 那好 我们可以超这个方向继续往下想 如果结论果真是如此的话 那么第一对关系肯定不符合 因为String和Integer压根不存在父子关系 只有第二对中的String和Object之间存在父子关系 好 接着往下想 如果是父子关系的话 多态是可以编译通过 也就是说 strBox和objBox指向的是同一个对象 然后我通过objBox设置了所指对象的element值 他的类型为Object类型 然后通过strBox获取所指对象的element值 并且通过一个String变量进行获取 但是我们可以清楚的发现由于之前element已经被设置为Object类型 所以导致Object赋值给了String 其实如果没有强制转换的话 这个语句是不能通过编译的 但是实际上这个语句是编译通过的 所以说我们刚才的strBox和objBox之间是不存在父子关系的
public class Main {public static void main(String[] args) {Box<String> strBox = new Box<>();Box<Integer> intBox = new Box<>();Box<Object> objBox = new Box<>();strBox = intBox;// errorobjBox = strBox;// error// 如果上面代码是正确的话 那么思考一下下面的代码objBox.setElement(new Object());String str = strBox.getElement();}
}
所以说下面这张图其实清楚的阐明了Number和Integer以及Box之间的关系
接着一个案例
我们都知道jdk中存在这这样如图所示的这样一种关系
由于上述各种接口或者类之间存在这父子关系 所以以下的代码其实都是可以编译通过的
public class Main {public static void main(String[] args) {Iterable<String> it = null;Collection<String> col = null;List<String> li = null;ArrayList<String> al = null;it = col;col = li;li = al;}
}
但是如果是以下代码的话 就不能够编译通过了
假设我们的List和ArrayList都是自定义的(就是非jdk源码 是允许的) 并且他们类或者接口的结构是一样的 那么如果按照刚才那个反例来看的话 那么list和al是父子关系 那么list和al所指对象是一致的(假设list的泛型为Object al的泛型是String) 并且我通过list设置所指对象的no 这个no就是Object类型的 然后通过al获取所指对象的no 并且通过String变量保留 而Object是不能够在没有强制转换的前提下完成这个赋值的 但是实际上这个操作是允许的 所以说list和al其实不是父子关系
public class Main {public static void main(String[] args) {List<Object> list = null;ArrayList<String> al = null;list = al;// error}
}
我可以总结一下就是说
当泛型不一致的话 那么泛型类型就一定不存在父子关系
当泛型一致的话 那么泛型类型也不一定存在父子关系 只有当泛型类型是有继承或者实现关系的话 那么泛型类型才有父子关系
前面说到泛型可以支持连续多个的 当我们两个类或者接口中的第一个泛型是一致的 并且子类可以存在第二个泛型的时候 那么这时候子类和父类之间是存在父子关系的
public interface MyList<E, T> extends List<E> {void setNo(T no);
}
public class Main {public static void main(String[] args) {List<String> li = null;MyList<String, Integer> ml1 = null;MyList<String, Double> ml2 = null;MyList<String, String> ml3 = null;li = ml1;li = ml2;li = ml3;}
}
从结果显示 编译是通过的 说明其实上述的这种操作是完全被允许的
3.原始类型
何为原始类型 其实就是没有为泛型提供一个具体的类型
从上图中 我们想说的是:
当我们使用了原始类型的时候 那么编译器就会警告我们这是一个原始类型 发出rawtypes警告
当我们将非原始类型赋值给原始类型的时候 编译器是很正常的
但是当我们将原始类型赋值给非原始类型的时候 编译器会发出unchecked警告
我们对于以上这些由编译器发出的警告可以通过@SuppressWarnings这个注解进行消除
public class Box<T>{private T no;public T getNo(){return no;}public void setNo(T no){this.no = no;}
}
public class Main {public static void main(String[] args) {Box box = new Box();box.setNo(new Object());Object o = box.getNo();}
}
通过上面这段代码 我想说的是如果是原始类型的话 那么当我们进行getter和setter方法的时候 所涉及到的no都是Object类型的 那么我们可能会有一个疑惑 就是这不是和泛型为Object的泛型类型一样吗 那么这两个等价吗 其实他们两者是有着本质的区别的:
一个是原始类型 一个是非原始类型 只是原始类型的getter和setter方法中涉及到的no都是Object类型的