黑马程序员 java基础 集合框架之泛型

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

泛型的产生

在jdk1.5版本以后,集合中可以存储基本的数据类型,因为java可以自动的对基本数据进行装箱,这样不适当的操作就会造成程序的一些安全隐患。

请看下面的代码:

import java.util.*;
class JavaCollection1_15 
{
	public static void main(String[] args) 
	{
		ArrayList al=new ArrayList();
		al.add("abc");
		al.add("bcd");
		al.add("xt");
		al.add(4);//注意java会对基本数据类型进行自动装箱,相当于al.add(new Integer(4));
		for(Iterator it=al.iterator();it.hasNext();)
		{
			String s=(String)it.next();
			System.out.println(s.length());
		}
	}
}
注意这段代码在编译时,能够通过,但是在运行时就会报异常, 运行结果如下:


通过提示我们可以发现,程序在运行时会报出类型转换错误,原因就是我们一开始对集合没有定义数据类型而造成。为了解决这种安全隐患,因此出现了泛型。

泛型的概述:

对集合(容器)一开始就定义了数据类型,是Jdk1.5版本后的一个新特性,用于解决安全隐患。

优点:

1 将运行时期的问题和安全隐患暴露在编译时期,减少了安全问题。

2避免了强制转换的麻烦,

上面的代码进行修改:

import java.util.*;
class JavaCollection1_15 
{
	public static void main(String[] args) 
	{
		ArrayList<String> al=new ArrayList<String>();
		al.add("abc");
		al.add("bcd");
		al.add("xt");
		al.add(4);//注意java会对基本数据类型进行自动装箱,相当于al.add(new Integer(4));
		for(Iterator<String> it=al.iterator();it.hasNext();)
		{
			//String s=(String)it.next();
			String s=it.next();
			System.out.println(s.length());
		}
	}
}

运行结果如下:


这时编译就不能通过,这样就可以提醒程序员能够修改程序。

泛型的使用
其实通过上面的程序我们可以发现<>就是用来接收数据的类型的。同时<E>中E的类型必须是引用类型的。

泛型实例三:

import java.util.*;
class JavaCollection1_16 
{
	public static void main(String[] args) 
	{
		TreeSet<String> ts=new TreeSet<String>(new MyComparator());
		ts.add("c");
		ts.add("aaa");
		ts.add("b");
		ts.add("bca");
		for(Iterator<String> it=ts.iterator();it.hasNext();)
		{
			String s=it.next();//这里避免了类型强制转换
			System.out.println(s);
		}
	}
}
class MyComparator implements Comparator<String>//泛型避免了强制转换,这里我们也可以定义泛型类型的比较器
{
	public int compare(String str1,String str2)
	{
       int num=str1.length()-str2.length();
	   if(num==0)
		   return str1.compareTo(str2);
	   return num;
	}
}
程序运行结果如下:


从上面的实例我们发现Comparator和Comparable都可以定义成泛型的,从而避免了类型强制转换。

泛型类

什么时候定义泛型类呢?

当类中要操作的引用数据类型不确定时,需要定义泛型类。

早期使用Object类型进行扩展,现在使用泛型更方便。

泛型实例四:

需求,定义一个工具类,用来操作不同的实例对象。

import java.util.*;
class JavaCollection1_17 
{
	public static void main(String[] args) 
	{
		Tools<Student> t=new Tools<Student>();
		t.setInstance(new Student());
		Student s=t.getInstance();
		Tools<Worker>tt=new Tools<Worker>();
		tt.setInstance(new Worker());
		Worker w=tt.getInstance();
	}
}
/*泛型类的定义用来操作不同的对象*/
class Tools<T>
{
	private T t;
	public void  setInstance(T t)
	{
		this.t=t;
	}
	public T getInstance()
	{
		return t;
	}
}
class Student
{
	
}
class  Worker
{
}

编译通过,说明上面的Tools类具有操作不同的引用类型对象的功能。

注意:泛型类定义的泛型在整个类中都有效,如果在方法中使用,那么在泛型类在创建对象时,明确了对象的操作类型,那么方法的操作类型也被局限性了

泛型方法

为了解决泛型类的局限性,定义一个方法,能够操作不同引用类型的数据,从而引进了泛型方法来解决类似的问题,泛型方法的定义是将泛型定义在方法体上,同时泛型方法也可以存在泛型类中。

实例五:

import java.util.*;
class JavaCollection1_18 
{
	public static void main(String[] args) 
	{
		TextDemo<Integer> t=new TextDemo<Integer>();
		t.show(new Integer("111"));
		t.show("123213");
		t.display(new Character('c'));
		t.print(new Integer("123"));
	}
}
//定义了泛型类,并在泛型类中定义了泛型方法,但是泛型类在实例化后,泛型的操作类型只能够限定print方法,而不能够限定show()和display()等类似的泛型方法。
class TextDemo<Q>
{
	public <T> void show(T t)
	{
		System.out.println(t);
	}
	public <G> void display(G g)
	{
		System.out.println(g);
	}
	public void print(Q q)
	{
		System.out.println(q);
	}
}

运行结果:


注意:特殊之处,静态方法不可以访问定义在类上的定义的泛型,如果静态方法操作的数据类型不确定,那么可以将泛型定义在泛型方法上。

其他发型知识点:

泛型接口

泛型接口在项目中的使用频率都很小,一般都使用内置的接口,但我们还是要了解其基本用法。

泛型实例六:

class  JavaCollection1_19
{
	public static void main(String[] args) 
	{
		MyGenericClass<String> mgc=new MyGenericClass<String>();
		mgc.show("aaaa");
	}
}
interface IText<T>
{
	void show(T t);
}
class MyGenericClass<T> implements IText<T>
{
	public void show(T t)
	{
		System.out.println(t);
	}
}
运行结果:



泛型限定


提到泛型限定,不得不提java中?通配符,我们可以理解为占位符,我们可以理解为一个占位符,在泛型中可能出现这种情况,我们只知道操作的集合时泛型的,但是具体是什么样的泛型我们不知道,这时我们在写方法时就可以使用?来作为占位符:

泛型实例七:

import java.util.*;
class JavaCollection1_20 
{
	public static void main(String[] args) 
	{
		ArrayList<String> al1=new ArrayList<String>();
		al1.add("aaa");
		al1.add("bbb");
		al1.add("ccc");
		ArrayList<Integer> al2=new ArrayList<Integer>();
		al2.add(1);
		al2.add(2);
		al2.add(3);
		show(al1);
		show(al2);
	}
	public static void show(ArrayList<?> al)
	{
		for(Iterator<?> it=al.iterator();it.hasNext();)
		{
			System.out.println(it.next());
		}
	}
}
运行结果是:

从上面的例子中我们可以看出,?可以起到占位的作用,只有等到使用时才知道?具体代表什么泛型类型。

接下来我们再看下一种情况,有时候我们需要一个方法只能够操作子类对象及其父类对象,其它的如果作为参数传进来就会报错,也就是限定了泛型的类型

实例八:

import java.util.*;
class JavaCollection1_21 
{
	public static void main(String[] args) 
	{
		ArrayList<Person> alp=new ArrayList<Person>();
		alp.add(new Person("xt_1",12));
		alp.add(new Person("xt_2",12));
		alp.add(new Person("xt_3",12));
		alp.add(new Person("xt_4",12));

		ArrayList<Student> als=new ArrayList<Student>();
		als.add(new Student("x",20));
		als.add(new Student("t",21));
		als.add(new Student("x",20));
		als.add(new Student("m",24));
		show(alp);
		show(als);

	}
	//定义了一个方法,这个方法采用了泛型限定,只允许传入的泛型操作对象是Person类型或者是Person类的子类对象。
	public static void show(ArrayList<? extends Person> al)
	{
		for(Iterator<? extends Person> it=al.iterator();it.hasNext();)
		{
			Person p=it.next();
			System.out.println(p.getName()+":"+p.getAge());
		}
	}
}
class Person
{
	private int age;
	private String name;
	Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	public int getAge()
	{
		return age;
	}
	public String getName()
	{
		return name;
	}
}
class Student extends Person
{
	Student(String name,int age)
	{
		super(name,age);
	}
}
通过我们查询javaAPI帮助文档我们可以发现TreeSet的构造函数中有 TreeSet(Comparator<? super E> comparator)这样的方法,而这个方法翻译过来就是,我们可以创建一个比较器,同时我们可以限定这个比较器的操作类型,

实例九:

需求:定义一个比较器,这个比较器不仅能让存储在TreeSet中的学生进行排序,并且这个比较器能够让存储在TreeSet中的工人进行排序,代码如下:

import java.util.*;
class JavaCollection1_22 
{
	public static void main(String[] args) 
	{
		//想要存储在TreeSet中的对象具备比较性,那么我们可以让TreeSet具备比较性,但是由于TreeSet中存储的对象不同,所以我们每次在初始TreeSet时,必须重新定义一个比较器,这样不利于代码的复用性,因此我们可以对比较器进行向上限定,
		
		TreeSet<Person> alp=new TreeSet<Person>(new MyCom());
		alp.add(new Person("xt_1",12));
		alp.add(new Person("xt_2",12));
		alp.add(new Person("xt_3",12));
		alp.add(new Person("xt_4",12));
		for(Iterator<Person> it=alp.iterator();it.hasNext();)
		{
			Person p=it.next();
			System.out.println(p.getName()+":"+p.getAge());
		}

		TreeSet<Worker> als=new TreeSet<Worker>(new MyCom());
		als.add(new Worker("wx",20));
		als.add(new Worker("wt",21));
		als.add(new Worker("ww",20));
		als.add(new Worker("wm",24));
		for(Iterator<Worker> it=als.iterator();it.hasNext();)
		{
			Worker p=it.next();
			System.out.println(p.getName()+":"+p.getAge());
		}


	}
}

class MyCom implements Comparator<Person>
{
	public int compare(Person p1,Person p2)
	{
		return p1.getName().compareTo(p2.getName());
	}
}
class Person
{
	private int age;
	private String name;
	Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	public int getAge()
	{
		return age;
	}
	public String getName()
	{
		return name;
	}
}
class Student extends Person
{
	Student(String name,int age)
	{
		super(name,age);
	}
}
class Worker extends Person
{
	Worker(String name,int age)
	{
		super(name,age);
	}
}
运行的结果如下:



通过上面的结果,我们发现只定义了一个比较器,由于限定了类型,Person类既是Worker类的基类,也是Student类的基类满足向上限定的条件,所以扩展了代码的复用性。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值