<span style="font-family: SimHei; background-color: rgb(255, 255, 255);"> </span><span style="font-family: SimHei; background-color: rgb(255, 255, 255);"> </span><span style="font-family:SimHei;font-size:24px;color:#ff0000;">泛型初级</span>
<span style="font-family: SimHei; background-color: rgb(255, 255, 255); font-size: 18px;"><span style="white-space:pre"> </span>泛型的由来:</span>
集合中可以存储任意类型对象,但是在取出时,如果要使用具体对象的特有方法时,
需要进行向下转型,如果存储的对象类型不一致,在转型过程中就会出现ClassCastException异常。
这样就给程序带来了不安全性。
在jdk1.5以后就有了解决方案——泛型技术:在存储元素时,就不允许存储不同类型的元素。
存储了就编译失败。 所以就需要在存储元素时,在容器上明确具体的元素类型,这其实和数组定义很像。
泛型的好处:
1)将运行时期的ClassCastException异常转移到了编译时期,进行检查,并以编译失败来体现。 这样有利于程序员尽早解决问题。(不会再运行中出现异常,在编译时候就能够发现错误)(泛型规定不能只能是自己的类型或者是子类)
2)避免了向下转型(强转)的麻烦。
代码说明一切:
package cn.hncu.generic;
//泛型能够限制类型。
public class genericdemo1 {
public static void main(String[] args) {
Mygeneric<Worker> generic =new Mygeneric<Worker>();
generic.add(new Worker());
//generic.add(00); 自己定义了泛型,只能是Worker类型的
// generic.add(new Student());//同样的报错,只能添加相同类型的数据
System.out.println(generic);
Mygeneric<Student> s=new Mygeneric<Student>();
s.add(new Student());
// s.add(new Worker());//编译通不过,出错,只能添加泛型定义的数据
}
}
class Mygeneric <QQ>{//自己定义泛型,随便写写,最重要的是<>里面的内容必须与下面的一致,内容随便写都可以
QQ obj;
public void add(QQ obj){//由于前面限制,所以只能添加QQ类型的数据
System.out.println("add:"+obj);
}
public QQ out(){
return obj;
}
@Override
public String toString() {
return "Mygeneric [obj=" + obj + "]";
}
}
class Worker{
}
class Student{
}
使用泛型的动机举例(以集合为例):
对集合中存放元素的类型进行限定,防止后期出错。如果限定某个集合只能存放Person类对象(因为后期会把元素取出来当作Person对象来处理),这样放其它类型的对象在编译时就会报错。相当于把一个类泛化成很多个不同类型(具体化,限定化)的类。泛型使用的代码如:
List<Person> persons = new ArrayList<Person>;
Map<String,String> m = new HashMap<String,String>;
注意:当一个变量被声明为泛型时,只能被实例变量和方法调用,而不能被静态变量和方法调用。原因很简单,参数化的泛型是一些实例。静态成员是被类的实例和参数化的类所共享的,所以静态成员不应该有类型参数和他们关联。 当一个类要操作的引用数据类型不确定的时候,可以将该类型定义一个形参。用到的这类时,由使用者来通过传递类型参数的形式,来确定要操作的具体的对象类型。 什么时候使用泛型类呢?只要类中操作的引用数据类型不确定的时候,就可以定义泛型类。 有了泛型类,省去了曾经的强转和类型转换异常的麻烦。
部分代码:
class Mygen<E>{
public void add(E e){
System.out.println(e.toString());
}
public Object out(Object obj){//方法不带泛型,当返回时候需要强转,不是很安全
return obj;
}
public <A> A myout(A a){//方法带泛型,但要求和类的泛型相互独立。可以限定返回类型和方法的实参相同,更安全,而且不用强转。
System.out.println("myout:" +a);
return a;
}
// //静态方法带泛型,泛型一定要独立于类,因为它没有对象。
public static <E> E myout1(E a){
System.out.println("myout:" +a);
return a;
}
完整简单代码演示《细节都在代码解释中》:
package cn.hncu.generic;
public class genericdemo2 {
public static void main(String[] args) {
Mygen<String > my =new Mygen<String>();
my.add("abc");
//my.add(21);//编译错误,泛型规定只能使用String类型的
String s=(String) my.out("asd");//方法不带泛型,返回值需要强转,不是很安全
//返回值也必须是泛型类型; 泛型很安全
//方法带泛型,但要求和类的泛型相互独立。可以限定返回类型和方法的实参相同,更安全,而且不用强转。
int m=my.myout(11);//myout:11;返回值和传进去的参数类型必须一致;
char c=my.myout1('a');
}
}
class Mygen<E>{
public void add(E e){
System.out.println(e.toString());
}
public Object out(Object obj){//方法不带泛型,当返回时候需要强转,不是很安全
return obj;
}
public <A> A myout(A a){//方法带泛型,但要求和类的泛型相互独立。可以限定返回类型和方法的实参相同,更安全,而且不用强转。
System.out.println("myout:" +a);
return a;
}
// //静态方法带泛型,泛型一定要独立于类,因为它没有对象。
public static <E> E myout1(E a){
System.out.println("myout:" +a);
return a;
}
}
当某些类用的接口泛型时候,我们也能解决,要求返回和类一样和不一样的类型的泛型都能解决,详见线面代码:
package cn.hncu.generic;
public class genericdemo2 {
public static void main(String[] args) {
Mygen<String > my =new Mygen<String>();
my.add("abc");
//my.add(21);//编译错误,泛型规定只能使用String类型的
String s=(String) my.out("asd");//方法不带泛型,返回值需要强转,不是很安全
//返回值也必须是泛型类型; 泛型很安全
//方法带泛型,但要求和类的泛型相互独立。可以限定返回类型和方法的实参相同,更安全,而且不用强转。
int m=my.myout(11);//myout:11;返回值和传进去的参数类型必须一致;
char c=my.myout1('a');
}
}
class Mygen<E>{
public void add(E e){
System.out.println(e.toString());
}
public Object out(Object obj){//方法不带泛型,当返回时候需要强转,不是很安全
return obj;
}
public <A> A myout(A a){//方法带泛型,但要求和类的泛型相互独立。可以限定返回类型和方法的实参相同,更安全,而且不用强转。
System.out.println("myout:" +a);
return a;
}
// //静态方法带泛型,泛型一定要独立于类,因为它没有对象。
public static <E> E myout1(E a){
System.out.println("myout:" +a);
return a;
}
}
泛型高级:
泛型的通配符:?
当操作的不同容器中的类型都不确定的时候,而且使用的都是元素从Object类中继承的方法,
这时泛型就用通配符?来表示即可。(助理解的比方: 泛型中的多态应用)。
当我们的集合中输出的两个集合是父子关系,就可以采用通配符输出(最简单的比喻):代码如下
package cn.hncu.generic;
import java.util.ArrayList;
import java.util.Iterator;
public class genericPrint {
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<String>();
list.add("qwe");
list.add("qw1e");
list.add("qw2e");
list.add("qw3e");
ArrayList<Integer> list1=new ArrayList<Integer>();
list1.add(1);
list1.add(2);
list1.add(3);
list1.add(4);
list1.add(5);
print(list);
print(list1);
}
private static void print(ArrayList<?> list) {//
Iterator<?> it =list.iterator();
while(it.hasNext())
{
Object obj =it.next();
System.out.println(obj);
}
}
//因为这是运行期的多态,而我们的泛型在编译期
// private static void print(ArrayList<Object> list) {
// Iterator<Object> it=list.iterator();
// while(it.hasNext()){
// Object obj =it.next();编译通不过;
// System.out.println(obj);
// }
//
// }
}
还有就是用到上限与下限
演示上限或者下限的应用体现(以TreeSet容器为例):
TreeSet的构造方法:
TreeSet(Collection<? extends E> c)
TreeSet(Comparator<? super E> comparator)
一般情况下:
只要是往容器中添加元素时,使用上限。 ? extends E
只要是从容器中取出元素时,是用下限。 ? super E
如在如下代码中,person为父类,Studentdemo为子类,当要求两个一起输出时候,可以采用上限,当要求比较大小采用通用的比较器的进行比较就可以用到下限,因为所有的子类都可以采用服类对象进行比较,具体代码如下:
package cn.hncu.generic;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
public class genericTopanddown {
public static void main(String[] args) {
// extendsdemo();//上限演示
superdemo();
}
private static void extendsdemo() {
ArrayList<Person> col=new ArrayList<Person>();
col.add(new Person("aa", 12));
col.add(new Person("bb", 19));
col.add(new Person("cc", 14));
/*
* boolean addAll(int index, Collection<? extends E> c)
将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。
*/
//能够实现一次性输出具有继承关系的对象
ArrayList<Studentdemo> cols=new ArrayList<Studentdemo>();//泛型的上限,<? extends E>
cols.add(new Studentdemo("ee", 15));
cols.add(new Studentdemo("ff", 16));
cols.add(new Studentdemo("gg", 17));
// col.addAll(cols);//这样写不安全,我们是已知二者存在的关系下,严谨必须在使用一个集合放相同的类型
//外加排序 用TreeSet
TreeSet<Person> set=new TreeSet<Person>();//必须实现compare接口
set.addAll(col);
set.addAll(cols);
Iterator<Person> it=set.iterator();//迭代器遍历
while(it.hasNext()){
Person p=it.next();
System.out.println(p);
}
}
//下限演示
private static void superdemo() {
TreeSet<Studentdemo> set =new TreeSet<Studentdemo>(new sortByName());//做一个比较器,person的子类都能比较
set.add(new Studentdemo("aja", 11));
set.add(new Studentdemo("aja1", 12));
set.add(new Studentdemo("aja3", 1));
set.add(new Studentdemo("aja22", 10));
Iterator<Studentdemo> it =set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//只要是person 的子类或是自己类,都可以通过(new sortByName())进行比较。这就是下限 ? super E
}
}
class Person implements Comparable<Person>{
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Person o) {
return this.age-o.age;
}
}
class Studentdemo extends Person{
String name;
int age;
public Studentdemo(String name,int age){
super(name,age);
}
}
//TreeSet中的按名字比较的比较器(persom)通用。
class sortByName implements Comparator<Person>{
public int compare(Person o1, Person o2) {
return o1.toString().compareTo(o2.toString());
}
}