java泛型机制

知识目标

1. 理解Java泛型的应用场景、泛型类和泛型方法。

2. 掌握Java泛型类、泛型接口和泛型方法的使用。

能力目标

熟练使用Java泛型类、泛型接口和泛型方法等编写相应的应用程序。

素质目标

1. 能够阅读科技文档和撰写分析文档。

2. 能够查阅JDK API。

3. 增强学生团队协作能力。

应用场景

使用Java泛型:能够对整型数组、字符串数组甚至其他任何类型的数组进行排序

使用Java泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序:在使用框架SSH(Struts+Spring+Hibernate)开发一个应用系统中,常使用DAO(Date AccessObject)来访问数据库对象,完成数据库中的数据和Java对象里的一种关联关系的一系列操作CRUD。数据库中的对象有很多,每一个对象都写一个DAO,显得很烦琐,每一个DAO都要写CRUD操作,这样代码的重复率高,如果使用泛型,代码的复用得到了很好的应用,提高了代码的效率。

相关知识

泛型的概念

泛型就是“宽泛的数据类型”,任意的数据类型。泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用。使用泛型可以很好地解决“代码复用”问题。

泛型的定义和使用

1.定义泛型类

在定义带类型参数的类时,在紧跟类名之后的<>内,指定一个或多个类型参数的名字,同时也可以对类型参数的取值范围进行限定,多个类型参数之间用“,”号分隔。定义完类型参数后,可以在定义位置之后的类的几乎任意地方(静态块、静态属性、静态方法除外)使用类型参数,就像使用普通的类型一样。注意,父类定义的类型参数不能被子类继承。

punlic class TestClassDefine<T,S extends T>{
    ..........
}

2.泛型方法

public <T,S extends T> T testGenericMethodDefine(T t,S s){
。。。。
}

3.泛型接口

先定义泛型接口:

public interface Generator<T>{
    public T next();
}

然后定义这个实现类来实现这个接口:

public class GeneratrImp1 implements Generator<String>{
    @override
    public String next(){
    ..............
}
}

 相关概念

1.通配符

类型通配符一般是使用“?”代替具体的类型参数,对类型参数赋予不确定值。例如List<?>在逻辑上是List<String>、List<Integer>等所有List<具体类型实参>的父类。

public class GenericTest{
    public static void main(String[] args){
        List<String> name = new ArrayList<String>();
        List<Integer> age = new ArrayList<Integer>();
        List<Number> number = new ArrayList<Number>();
        name.add("zhangsan");
        age.add(18);
        number.add(341);

        getData(name);
        getData(age);
        gatData(number);    
    }
    public static void getData(Liat<?> data){
    System.out.println("data:"+data.get(0));
}
}



//data:zhangsan
data:18
data:341

2.上下边界:限制使用泛型类别

如果想限制使用泛型类别时,只能用某个特定类型或者是其子类型才能实例化该类型时,可以在定义类型时,使用extends关键字指定这个类型必须是继承某个类,或者实现某个接口,也可以是这个类或接口本身。

(1)类型通配符上限通过List来定义,如此定义就是通配符泛型值接受Number及其下层子类类型。

public class GenericTest{
    public static void main(String[] args){
        List<String> name = new ArrayList<String>();
        List<Integer> age = new ArrayList<Integer>();
        List<Number> number = new ArrayList<Number>();
        name.add("zhangsan");
        age.add(18);
        number.add(341);

        getData(name);
        getData(age);
        gatData(number);  

        getUperNumber(name);//........1
        getUperNumber(age);//.........2
        getUperNumber(number);//......3  
    }
    public static void getData(Liat<?> data){
    System.out.println("data:"+data.get(0));
}
    public static void getUperNumber(List<? extends Number> data){
        System.out.println("data:"+data.get(0));
}
}



//1处报错

解析:在//1处会出现错误,因为getUperNumber()方法中的参数已经限定了参数泛型上限为Number,所以泛型为String不在这个范围之内,所以会报错。

(2)类型通配符下限通过形如List<? extendsNumber>来定义,表示类型只能接受Number及其三层父类类型,如Objec类型的实例。

3.擦除

ava中的泛型基本上都是在编译器这个层次来实现的。生成的Java字节码中,是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,编译器在编译的时候去掉,这个过程就称为类型擦除。如在代码中定义List<Integer>和List<String>等类型,在编译后都会编成List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。

泛型的好处

(1)类型安全

通过知道使用泛型定义的变量的类型限制,编译器可以更有效地提高Java程序的类型安全。

(2)消除强制类型转换

消除源代码中的许多强制类型转换。这使得代码可读性更强,并且减少了出错机会。所有的强制转换都是自动和隐式的。

(3)提高性能

List list1 = new ArrayList();
list1.add("wangzhikang");
String str1 = (String)list.get(0);

List<String> list2 = new ArrayList<String>();
list2.add("wangzhikang");
String str2 = list2.get(0);

对于上面的两段程序,由于泛型所有工作都在编译器中完成,Javac编译出来的字节码是一样的(只是更能确保类型安全),那么何谈性能提升呢?是因为在泛型的实现中,编译器将强制类型转换插入生成的字节码中,但是更多类型信息可用于编译器这一事实,为未来版本的JVM的优化带来了可能.

泛型使用中的注意事项

(1)泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。

(2)泛型的类型参数可以有多个

任务实施:

任务一:泛型类的定义和使用

1.任务需求

先来看没有泛型的情况下的容器类如何定义:

package 泛型;
//没有泛型的容器类
public class Boxs {
    private String key;
    private String value;
    public Boxs(String key,String value){
        this.key = key;
        this.value = value;
    }
    public String getKey(){
        return key;
    }
    public void setKey(String key){
        this.key = key;
    }
    public String getValue(){
        return value;
    }
    public void setValue(String value){
        this.value = value;
    }
}

####缺点:Box类保存了一对key-value键值对,但是类型是定死的,也就说如果我想要创建一个String-Integer类型的键值对,当前这个Box是做不到的,还必须要另外重写一个Box,这明显重用性就非常低,代码得不到复用,使用泛型可以很好地解决这个问题。

2.任务分析

重新定义Box类,类图如图4-1所示。

 3.任务实现

Box.java

package 泛型;
//带泛型的容器类
public class NewBoxs<K,V> {
    private K key;
    private V value;
    public NewBoxs(K k,V v){
        this.key = k;
        this.value = v;
    }
    public K getKey(){
        return key;
    }
    public void setkey(K k){
        this.key = k;
    }
    public V getValue(){
        return value;
    }
    public void setValue(V v){
        this.value = v;
    }
}

在编译期是无法知道K和V具体是什么类型的,只有在运行时才会真正根据类型来构造和分配内存。这样我们的Box类便可以得到复用,我们可以将T替换成任何我们想要的类型。实例化泛型类的时候,我们只需要把类型参数换成具体的类型即可。可以看一下现在Box类对于不同类型的支持情况

TestBox.java

package 泛型;

public class TestBox {
    public static void main(String[] args) {
        NewBoxs<String,String> b1 = new NewBoxs<>("name","zhangsan");
        NewBoxs<Integer,String> b2 = new NewBoxs<>(1,"lisi");
        NewBoxs<Double,Double> b3 = new NewBoxs<>(1.2,1.3);
        System.out.println(b1.getKey()+":"+b1.getValue());
        System.out.println(b2.getKey()+":"+b2.getValue());
        System.out.println(b3.getKey()+":"+b3.getValue());

    }
}

 说明

通过public class Container<K, V> {}定义泛型类,在实例化该类时,必须指明泛型K、V的具体类型,例如:Container<String,String> c1 = new Container<String,String>("name", "Messi");,指明泛型K的类型为String,泛型V的类型为String。

任务二:泛型方法的定义和使用

1.任务需求

定义和使用泛型方法。

2.任务分析

声明一个泛型方法很简单,只要在返回类型前面加上一个类似<K, V>的形式就行了。其类图如图4-3所示。

 

 

 3.任务实现

Pair.java

package 泛型;

public class Pair<K,V> {
    private K key;
    private V value;

    public Pair(K k,V v){
        this.key = k;
        this.value = v;
    }
    public K getKey(){
        return key;
    }
    public V getValue(){
        return value;
    }
    public void setKey(K k){
        this.key = k;
    }
    public void setValue(V v){
        this.value = v;
    }
}

Util.java

package 泛型;

public class Util {
    public static <K,V> boolean compare(Pair<K,V> p1,Pair<K,V> p2){
        return p1.getKey().equals(p2.getKey())&&p1.getValue().equals(p2.getValue());
    }
}

TestUtil.java

package 泛型;

public class TestUtil {
    public static void main(String[] args) {
        //TODO Auto-generated method stub
        Pair<String,String> p1 = new Pair<>("name","zhangsan");
        Pair<String,String> p2 = new Pair<>("name","lisi");
        System.out.println("计较p1,p2:"+Util.compare(p1,p2));

        Pair<String,Integer> p3 = new Pair<>("age",18);
        Pair<String,Integer> p4 = new Pair<>("age",19);
        System.out.println("计较p3,p4:"+Util.compare(p3,p4));

        Pair<Integer,String> p5 = new Pair<>(1,"lisi");
        Pair<Integer,String> p6 = new Pair<>(2,"wangwu");
        System.out.println("计较p5,p6:"+Util.compare(p5,p6));


    }
}

 说明

在调用泛型方法的时候,在不指定泛型的情况下,泛型变量的类型为该方法中的几种类型的同一个父类的最小级,直到Object。在指定泛型的时候,该方法中的几种类型必须是该泛型实例类型或者其子类。

任务三 泛型接口的定义和使用

1.任务需求

对任两个数求和。这两个数可以是整数、浮点数和字符串。

2.任务分析

定义泛型接口Calculator,其中定义泛型方法and,用来求和。

3.任务实现

Calculator接口

package 泛型.泛型接口的定义和使用;
//
public interface Calculator <T>{
    public T and(T a,T b);
}

定义类CalculatorInteger实现Calculator接口。

package 泛型.泛型接口的定义和使用;

public class CalculatorInteger implements Calculator<Integer>{
    @Override
    public Integer and(Integer a,Integer b){
        //TODO Auto-generated method stub
        return a+b;
    }
}
定义类CalculatorString实现Calculator接口。
package 泛型.泛型接口的定义和使用;

public class CalculatorString implements Calculator<String>{
    @Override
    public String and(String a, String b) {
        //TODO Auto-generated method stub
        return a+b;
    }



}

定义测试类TestCalculator,进行测试。

package 泛型.泛型接口的定义和使用;

public class TestCalculator {
    public static void main(String[] args) {
        //TODO Auto-generated method stub
        CalculatorInteger ci = new CalculatorInteger();
        Integer and1 = ci.and(10, 20);
        System.out.println(and1);

        CalculatorString cs = new CalculatorString();
        String and2 = cs.and("湖北", "武汉");
        System.out.println(and2);
    }

}

运行结果:

 说明

通过public interface Calculator<T> {}定义泛型接口,在实现该接口时,必须指明泛型T的具体类型,例如:public classCalculatorInteger implementsCalculator<Integer>{},指明泛型T的类型为Integer,实例化该类时无须指定泛型类型。

参考书籍:java高级程序设计实战教程

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值