黑马程序员:Java基础——泛型,黑马程序员java
黑马程序员:Java基础——泛型,黑马程序员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文档,在Collection和Set中都有<?>以及<? extends E>、<? super E>的字样,这就是泛型限定。
?通配符,也可以理解为占位符。
泛型的限定:是用于泛型扩展
? extends E:可以接收E类型或者E的子类型。上限。
? super E:可以接收E类型或者E的父类型。下限。
示例:
我们先建三个类,Students,Workers,Persons,其中前两个都继承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
从结果中我们看到不管是比较器还是遍历都是有效的,而且这时我的比较器只需要写个父类就可以了。
相关文章
- 暂无相关文章
用户点评