1——泛型在集合中的使用
一、为什么会有泛型
先看下面的代码 Demo1:
class GenericDemo {
public static void main(String[] args) {
ArrayList al = new ArrayList();
al.add("abc01");
al.add("abc0991");
al.add("abc014");
al.add(4);
Iterator it = al.iterator();
while(it.hasNext()) {
String s = (String)it.next();
System.out.println(s+":"+s.length());
}
}
}
编译后出现这个提示:
运行后出现异常:
异常的原因是类型转换错误,Integer不能转换为String。 假如现在有一个程序,编译时期是没问题的,结果交到用户手上,一运行就出错,这是非常不安全的,所以编译的时候编译器给了一个提示,表示使用了不安全的操作。为了解决这种问题,Java在JDK1.5推出了泛型机制,在JDK1.5之前,程序员在使用集合的时候,只能主观的向集合中添加相同的元素,有了泛型之后,就可以强制添加相同的类型,如果类型不一致,在编译时期就会出错,这就大大提高了程序的安全性。接下来看看泛型到底怎么用。
Demo2:泛型的简单使用
class GenericDemo {
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<String>();
al.add("abc01");
al.add("abc0991");
al.add("abc014");
// al.add(4);
Iterator<String> it = al.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s+":"+s.length());
}
}
}
泛型格式:通过<>来定义要操作的引用数据类型。想要指定集合存储的类型,在new集合的时候就得把泛型写上,然后在迭代器那里,因为集合指定了类型,那么该集合的迭代器也得指定为该类型,写了泛型之后,迭代器里面就不需要强制转换了。 如果将上面代码的注释去掉,想加入一个非String类型的对象,那么编译时就会有错误
Demo3:泛型再比较器上的使用
class GenericDemo2 {
public static void main(String[] args) {
TreeSet<String> ts = new TreeSet<String>(new LenComparator());
ts.add("abcd");
ts.add("cc");
ts.add("cba");
ts.add("aaa");
ts.add("z");
ts.add("hahaha");
Iterator<String> it = ts.iterator();
while(it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
}
//加上泛型之后,比较器内部也不用强制转换,直接接受String类型就行了
class LenComparator implements Comparator<String> {
public int compare(String o1,String o2) {
int num = new Integer(o1.length()).compareTo(new Integer(o2.length()));
if(num==0)
return o1.compareTo(o2);
return num;
}
}
需要注意的是:在HashSet中,比较两个元素需要用到equals方法,这个不能使用泛型。因为equals属于object里面的方法,object不可以使用泛型。
2——泛型类、泛型方法
一、泛型类
什么时候定义泛型类?当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展。现在定义泛型来完成扩展。
Demo1:定义一个工具类,可以操作不同的类
class Worker {
}
class Student {
}
//泛型前做法。
class Tool {
private Object obj;
public void setObject(Object obj) {
this.obj = obj;
}
public Object getObject() {
return obj;
}
}
//泛型类。
class Utils<QQ> {
private QQ q;
public void setObject(QQ q) {
this.q = q;
}
public QQ getObject() {
return q;
}
}
class GenericDemo3 {
public static void main(String[] args) {
Utils<Worker> u = new Utils<Worker>(); //new一个工具类
u.setObject(new Worker());
// u.setObject(new Student()); //编译错误
Worker w = u.getObject();
/*
Tool t = new Tool();
t.setObject(new Worker());
t.setObject(new Student()); //编译不会出错,但是运行时出错
Worker w = (Worker)t.getObject();
*/
}
}
二、泛型方法
Demo2:
class Demo<T> {
public void show(T t) {
System.out.println("show:"+t);
}
public <Q> void print(Q q) {
System.out.println("print:"+q);
}
public static <W> void method(W w) {
System.out.println("method:"+ w);
}
}
class GenericDemo4 {
public static void main(String[] args) {
Demo <String> d = new Demo<String>();
d.show("haha");
//d.show(4);
d.print(5);
d.print("hehe");
Demo.method("hahahahha");
Demo.method(1);
}
}
1.泛型方法的泛型加在返回值前面,加到其他地方会出错 2.如果方法上没有加泛型,那么方法的类型与类的泛型一致,所以上面代码的show方法只能输出String,输出Integer就会报错 3.当类和方法都加了泛型且不一致时,以方法上的泛型为准,所以print方法可以输出多种类型 4.静态方法想要使用泛型必须在自己在方法上指定,不能使用类的泛型,因为类的泛型是在new的时候指定的,而静态方法在new之前就存在了
一、通配符
Demo1:<?>
class GenericDemo6 {
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<String>();
al.add("abc1");
al.add("abc2");
al.add("abc3");
ArrayList<Integer> al1 = new ArrayList<Integer>();
al1.add(4);
al1.add(7);
al1.add(1);
printColl(al);
printColl(al1);
printColl2(al);
printColl2(al1);
}
//使用通配符
public static void printColl(ArrayList<?> al) {
Iterator<?> it = al.iterator();
while(it.hasNext()) {
System.out.println(it.next());
// System.out.println(it.next().length()); //不能使用
}
}
//使用泛型方法
public static <T> void printColl2(ArrayList<T> al) {
Iterator<T> it = al.iterator();
while(it.hasNext()) {
T t = it.next(); //可以使用T
System.out.println(t);
}
}
}
通配符与泛型方法差不多,都可以接受不同的类型,不过泛型方法有可以具体使用的类型T,虽然不明确是什么类型,但是可以使用,而通配符就不可以了。使用泛型虽然有了拓展性,但是也有不可避免的弊端,那就是不能使用某些具体类中的特有方法,比如上面的length(),String类型有,而integer类型没有,所以不能使用。跟多态不能使用子类特有方法一个道理。
二、泛型的限定
<? extends E>: 可以接收E类型或者E的子类型。上限。
<? super E>: 可以接收E类型或者E的父类型。下限
Demo2:<? extends E>
class Person {
private String name;
Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Student extends Person {
Student(String name) {
super(name);
}
}
class GenericDemo6 {
public static void main(String[] args) {
ArrayList<Person> al = new ArrayList<Person>();
al.add(new Person("abc1"));
al.add(new Person("abc2"));
al.add(new Person("abc3"));
printColl(al);
ArrayList<Student> al1 = new ArrayList<Student>();
al1.add(new Student("abc--1"));
al1.add(new Student("abc--2"));
al1.add(new Student("abc--3"));
printColl(al1);
}
//打印方法使用了泛型限定
public static void printColl(ArrayList<? extends Person> al) {
Iterator<? extends Person> it = al.iterator();
while(it.hasNext()) {
System.out.println(it.next().getName());
}
}
}
/*
输出:
abc1
abc2
abc3
abc--1
abc--2
abc--3
*/
上面打印方法的泛型只写<?>是没有问题的,但是这样任何类型都可以传进来,容易产生问题。只写<Person>会出错,只能写<? extends Person>,这样Person和它的子类都可以传进来。这把最顶端的类型给限定了,叫上限。 还有种叫下限<? super E>,在集合的比较器中就有
TreeSet(Comparator<? superE> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。 |
Demo3:<? super E>
class GenericDemo7 {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<Student>(new Comp());
ts.add(new Student("abc03"));
ts.add(new Student("abc02"));
ts.add(new Student("abc06"));
ts.add(new Student("abc01"));
//遍历输出
Iterator<Student> it = ts.iterator();
while(it.hasNext()) {
System.out.println(it.next().getName());
}
TreeSet<Worker> ts1 = new TreeSet<Worker>(new Comp());
ts1.add(new Worker("wabc--03"));
ts1.add(new Worker("wabc--02"));
ts1.add(new Worker("wabc--06"));
ts1.add(new Worker("wabc--01"));
//遍历输出
Iterator<Worker> it1 = ts1.iterator();
while(it1.hasNext()) {
System.out.println(it1.next().getName());
}
}
}
/*笨方法
class StuComp implements Comparator<Student>
{
public int compare(Student s1,Student s2)
{
return s1.getName().compareTo(s2.getName());
}
}
class WorkerComp implements Comparator<Worker>
{
public int compare(Worker s1,Worker s2)
{
return s1.getName().compareTo(s2.getName());
}
}
*/
//简便方法
class Comp implements Comparator<Person> {
public int compare(Person p1,Person p2) {
return p2.getName().compareTo(p1.getName());
}
}
class Person {
private String name;
Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String toString() {
return "person :"+name;
}
}
class Student extends Person {
Student(String name) {
super(name);
}
}
class Worker extends Person {
Worker(String name) {
super(name);
}
}
/*
输出:
abc06
abc03
abc02
abc01
wabc--06
wabc--03
wabc--02
wabc--01
*/
在Comparator的泛型中,可以不写Student而写Person,因为Student继承了Person,比较时用的是父类的方法,所以Person可以接受子类对象并进行比较。 注意:<? super E>不像<? extends E>一样全部照抄,而必须写上确定的父类。