黑马程序员:Java基础——泛型

------- Java EE培训java培训、期待与您交流! ----------

1.概念

泛型:JDK1.5版本以后出现新特性。用于解决安全问题,是一个安全机制
好处:1.将运行时期出现的问题ClassCastException,转移到了编译时期。
       方便与程序员的解决问题,让运行时期问题减少,安全。
          2.避免了强制转换。

示例:

import java.util.ArrayList;
import java.util.Iterator;

import com.micronote.collection.SystemOutPrintClass;

public class GenericDemo extends SystemOutPrintClass{
    public static void main(String[] args) {
		ArrayList<String> al = new ArrayList<String>();
		
		al.add("Java01");
		al.add("Java02");
		al.add("Java03");
		al.add(2, "Java04");
		
		for(Iterator<String> it = al.iterator();it.hasNext();){
			sopln(it.next());
		}
	}
}

2.泛型的使用

那么我们的问题就来了:在使用Java提供的对象时,什么时候写泛型?
通常在集合框架中很常见。只要见到<>就要定义泛型。

其实<>就是用来接收类型的。
当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。

我们用Set的例子来进行修改: 

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

import com.micronote.collection.SystemOutPrintClass;

public class GenericDemo2 extends SystemOutPrintClass{
    public static void main(String[] args) {
		TreeSet<String> ts = new TreeSet<String>(new CompareStrLength());
		ts.add("abcd");
		ts.add("cc");
		ts.add("bcdef");
		ts.add("bb");
		ts.add("cde");
		ts.add("defghi");
	
		for(Iterator<String> it = ts.iterator();it.hasNext();){
			sopln(it.next());
		}
	}
}

class CompareStrLength implements Comparator<String>{

	@Override
	public int compare(String o1, String o2) {
		
		int iNum = new Integer(o1.length()).compareTo(o2.length());
		if(iNum==0){
			return o1.compareTo(o2);
		}else{
			return iNum;
		}
	}
}
我们看到,代码量明显减少,我们省去了很多强转 。

3.泛型类

接下来我们看一下泛型类,我们先写一个没有泛型类:

class Tools{
	private Object obj;

	public Object getObj() {
		return obj;
	}
	public void setObj(Object obj) {
		this.obj = obj;
	}
}

class Student{}
class Worker{}

public class GenericDemo3 {
    public static void main(String[] args) {
		Tools t = new Tools();
		t.setObj(new Student());
		Worker worker = (Worker)t.getObj();
	}
}

首先,要知道什么时候定义泛型类?

当类重要操作的引用数据类型不确定的时候,早期定义Object来完成可扩展。现在定义泛型来完成扩展。

通过运行我们发现,出现了ClassCastException,这是因为当setObj中传入的是Student时,后面使用的是如果是Worker,则会出现类型转换异常。而且要注意我们是在运行的时候才发现错误的。

那么,我们如果把泛型加上:

可以看到,在main函数中有红线标志,这代表我们在编译的时候就已经发现了错误,此时,我们可以针对错误进行修改。

我们把Student改成Worker后,红线没有了,证明编译正常,我们运行也没有任何问题。而且,但我们把t.getObj();前的强转去掉以后也没有报错,运行也正常。

4.泛型方法

泛型除了定义类还可以定义方法。因为泛型类定义的泛型,在整个类中有效。如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。而为了让不同方法可以操作不同的类型,而且类型还不确定。那么可以将泛型定义在方法上。

代码如下(注意:打印方法已被封装):

import java.security.PublicKey;

import com.micronote.collection.SystemOutPrintClass;

/*class Demo<T> extends SystemOutPrintClass{
	public void show(T t){
		sopln("show:"+t);
	}
	public void print(T t){
		sopln("print:"+t);
	}
}*/
class Demo extends SystemOutPrintClass{
	public <T> void show (T t){
		sopln("show:"+t);
	}
	public <Q> void print(Q q){
		sopln("print:"+q);
	}
}
public class GenericDemo4 {
    public static void main(String[] args) {
		Demo demo = new Demo();
		demo.show("haha");
		demo.show(new Integer(4));
		demo.print("Heihei");
		demo.print(9);
    	
    	/*Demo<Integer> d = new Demo<Integer>();
		
		d.show(new Integer(4));
		d.print(9);
		
		Demo<String> d1 = new Demo<String>();
		d1.print("hehe");
		d1.show("haha");*/
	}
}

输出结果如下:

show:haha
show:4
print:Heihei
print:9

也就是说,这时我们往泛型方法里面传什么,泛型就会变成什么,这样使用起来就比泛型类更为方便。

当然我们也可以在定义泛型类的同时定义泛型方法,在函数中调用的时候泛型方法依旧可以传入各种类型,而泛型类中的普通方法则是随着类中的泛型而决定的。

5.静态方法泛型

在上一节的基础上我们说一个特殊情况:

静态方法不可以访问类上定义的泛型。

如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。示例代码如下:

class Demo<T> extends SystemOutPrintClass{
	public void show (T t){
		sopln("show:"+t);
	}
	public <Q> void print(Q q){
		sopln("print:"+q);
	}
	public static <W> void method(W w){
		sopln("method:"+w);
	}
}
public class GenericDemo4 {
    public static void main(String[] args) {
		Demo<String> demo = new Demo<String>();
		demo.show("haha");
		demo.print("Heihei");
		demo.print(9);
		demo.method("Gagaga!");
	}
}

需要注意的是:泛型应该放置于返回类型前,修饰符后,否则视为格式错误。

5.泛型接口

首先我们来一个接口不知道类型,实现时指定的:

interface Inter<T>{
	void show(T t);
}
class InterImpl implements Inter<String>{
	public void show(String t){
		System.out.println("Show : "+t);
	}
}

public class GenericDemo5 {
    public static void main(String[] args) {
		InterImpl impl = new InterImpl();
		impl.show("Haha");
	}
}

通过编译运行,我们的代码正常。

但是当我在实现时也不知道要传的类型呢? 

我们也可以给实现类添加泛型:

interface Inter<T>{
	void show(T t);
}

class InterImpl<T> implements Inter<T>{
	public void show(T t){
		System.out.println("Show : "+t);
	}
}

public class GenericDemo5 {
    public static void main(String[] args) {
    	InterImpl<Integer> impl = new InterImpl<Integer>();
    	impl.show(4);
	}
}
当我们调用,确认,并输出时,代码并无编译以及运行错误。

6.泛型限定

通过前面的学习,我们发现泛型可以限定类型,可是,出现了局限性。依照前面Set Blog中我们编写自定义的泛型集合时,我们发现,我们每要打印遍历,或者比较的时候都需要再编写一个方法。这就造成了代码的重复多余。

我们打开API文档,在CollectionSet中都有<?>以及<? extends E><? super E>的字样,这就是泛型限定。

?通配符,也可以理解为占位符。

泛型的限定:是用于泛型扩展
? extends E:
可以接收E类型或者E的子类型。上限。
? super E:
可以接收E类型或者E的父类型。下限。

示例:

我们先建三个类,StudentsWorkersPersons,其中前两个都继承Persons

class Persons{
	private String name;
	Persons(){}
	Persons(String name){
		this.name = name;
	}
	public String getName(){
		return this.name;
	}
}

class Students extends Persons{
	Students(String name){
		super(name);
	}
}

class Workers extends Persons{
	Workers(String name){
		super(name);
	}
}

了便于直,我先用ArrayList演示<? extends E>:


import java.util.ArrayList;
import java.util.Iterator;

public class GenericDemo6 {
    public static void main(String[] args) {
		ArrayList<Persons> al = new ArrayList<Persons>();
		al.add(new Persons("abc1"));
		al.add(new Persons("abc2"));
		al.add(new Persons("abc3"));
		
		ArrayList<Students> al1 = new ArrayList<Students>();
		al1.add(new Students("abc--1"));
		al1.add(new Students("abc--2"));
		al1.add(new Students("abc--3"));
		
		printColl(al);
		printColl(al1);
	}
    
    public static void printColl(ArrayList <? extends Persons> al){
    	for(Iterator<? extends Persons> it = al.iterator();it.hasNext();){
    		System.out.println(it.next().getName());
    	}
    }
}

我们看到,当ArrayList后的泛型设置为<? extends Persons>时,运行结果为:

abc1
abc2
abc3
abc--1
abc--2
abc--3

不管是Persons还是Students都遍历出来了。

我们再用Set集合来一次:

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class GenericDemo7 {
    public static void main(String[] args) {
		TreeSet<Persons> pSet = new TreeSet<Persons>(new CompartorClass());
		pSet.add(new Persons("Persons01"));
		pSet.add(new Persons("Persons02"));
		pSet.add(new Persons("Persons03"));
		
		TreeSet<Students> sSet = new TreeSet<Students>(new CompartorClass());
		sSet.add(new Students("Students01"));
		sSet.add(new Students("Students02"));
		sSet.add(new Students("Students03"));
		
		TreeSet<Workers> wSet = new TreeSet<Workers>(new CompartorClass());
		wSet.add(new Workers("Workers01"));
		wSet.add(new Workers("Workers02"));
		wSet.add(new Workers("Workers03"));
		
		printColl(pSet);
		printColl(sSet);
		printColl(wSet);
	}
    
    public static void printColl(TreeSet <? extends Persons> pSet){
    	for(Iterator<? extends Persons> it = pSet.iterator();it.hasNext();){
    		System.out.println(it.next().getName());
    	}
    }
}

class CompartorClass implements Comparator<Persons>{

	@Override
	public int compare(Persons o1, Persons o2) {
		return o1.getName().compareTo(o2.getName());
	}
}

运行结果如下:

Persons01
Persons02
Persons03
Students01
Students02
Students03
Workers01
Workers02
Workers03

从结果中我们看到不管是比较器还是遍历都是有效的,而且这时我的比较器只需要写个父类就可以了。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值