Java泛型详解

简介: 笔记

一、泛型简介


Java 5之后提供泛型(Generics)支持,使用泛型可以最大限度地重用代码、保护类型的安全以及提高 性能。泛型特性对Java影响最大是集合框架的使用。

为了理解什么是泛型,请大家先看一个使用集合的示例:

package 泛型.list泛型集合方法;
import java.util.ArrayList;
import java.util.List;
//list泛型集合方法
public class HelloWorld {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
//        list.add(5); 发生编译错误
//        for遍历
        for (Object item:list){   //1
            Integer element = (Integer)item; //2
            System.out.println(item);
        }
    }
}

上述代码实现的功能很简单,就将一些数据保存到集合中,然后再取出。但对于Java 5之前程序员而 言,使用集合经常会面临一个很尴尬的问题:放入一个种特定类型,但是取出时候全部是Object类 型,于是在具体使用时候需要将元素转换为特定类型。上述代码第1行取出的元素是Object类型,在 代码第2行需要强制类型转换。强制类型转换是有风险的,如果不进行判断就臆断进行类型转换会发 生ClassCastException异常。本例代码第2行就会发生了这个异常,JVM会抛出异常,打印出如下的异 常堆栈跟踪信息。

在Java 5之前没有好的解决办法,在类型转换之前要通过instanceof运算符判断一下,该对象是否是目标类型。而泛型的引入可以将这些运行时异常提前到编译期暴露出来,这增强了类型安全检查。

修改代码如下:

package 泛型.list泛型集合方法;
import java.util.ArrayList;
import java.util.List;
//list泛型集合方法
public class HelloWorld {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
//        list.add(5); 发生编译错误
//        for遍历
        for (String item:list){
//            Integer element = (Integer)item;
            System.out.println(item);
        }
    }
}

由此可见泛型的作用:

1,类型安全。

泛型的主要目标是提高 Java 程序的类型安全。编译时的强类型检查;通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。

2,消除强制类型转换。

泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。

3,潜在的性能收益。

泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改。所有工作都在编译器中完成,编译器生成类似于没有泛型(和强制类型转换)时所写的代码,只是更能确保类型安全而已。
 Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

4、更好的代码复用性,比如实现泛型算法

在框架设计时候,BaseDao<T>、BaseService<T>、BaseDaoImpl<T>、BaseServiceImpl<T>;通过继承,实现抽象了所有公共方法,避免了每次都要写相同的代码。

二、使用泛型


泛型对于Java影响最大就是集合了,Java 5之后所有的集合类型都可以有泛型类型,可以限定存放到集 合中的类型。打开API文档,会发现集合类型后面会有,如:Collection、 List、ArrayList、Set和Map,这说明这些类型是支持泛型的。尖括号中的E、K和V 等是类型参数名称,它们是实际类型的占位符。


(1)List泛型集合

代码如下:

package 泛型.list泛型集合方法;
import java.util.ArrayList;
import java.util.List;
//list泛型集合方法
public class HelloWorld {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
//        list.add(5); 发生编译错误
//        for遍历
        for (String item:list){
//            Integer element = (Integer)item;
            System.out.println(item);
        }
    }
}


(2)Set泛型集合

代码如下:

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
//泛型的使用
// 测试Set泛型集合方法
public class HelloWorldGen {
    public static void main(String[] args) {
        Set<String> set = new HashSet<String>();
        // 向集合中添加元素
        set.add("A");
        set.add("D");
        set.add("E");
// 1.使用for-each循环遍历
        System.out.println("--1.使用for-each循环遍历--");
        for (String item :set){
            System.out.println(item);
        }
// 2.使用迭代器遍历
        System.out.println("--2.使用迭代器遍历--");
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()){
            String item = iterator.next();
            System.out.println("读取集合元素"+item);
        }
    }
}

(3)Map泛型集合

代码如下:

import java.util.*;
//Map泛型集合
public class HelloWorldGen {
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<Integer, String>();
        map.put(101,"A");
        map.put(102, "B");
        map.put(103, "C");
        map.put(104, "D");
        // 1.使用for-each循环遍历
        System.out.println("--1.使用for-each循环遍历--");
        // 获得键集合
        Set<Integer> keys = map.keySet();
        for (Integer key:keys ){
            String value = map.get(key);
            System.out.printf("key=%d - value=%s \n", key, value);
        }
//        2.使用迭代器遍历集合
        System.out.println("--2.使用迭代器遍历--");
       // 获得值集合
        Collection<String> values = map.values();
//        遍历值集合
        Iterator<String> iterator = values.iterator();
        while (iterator.hasNext()){
            String item = iterator.next();
            System.out.println("读取集合元素"+item);
        }
    }
}

三、自定义泛型类


根据自己的需要也可以自定义泛型类、泛型接口和带有泛型参数的方法。下面通过一个示例介绍一下 泛型类。数据结构中有一种“队列”(queue)数据结构

代码如下:

import java.util.ArrayList;
import java.util.List;
/**
 * 自定义的泛型队列集合
 * */
//自定义泛型类
public class Queue<T> {
    // 声明保存队列元素集合items
    private List<T> items;
// 构造方法初始化是集合items
    public Queue(){
        this.items = new ArrayList<T>();
    }
    /**
     * 入队方法
     * @param item 参数需要入队的元素 */
    public void queue(T item){
        this.items.add(item);
    }
    /**
     * 出队方法
     * @return 返回出队元素 */
    public T dequeue(){
        if(items.isEmpty()){
            return null;
        }
        else {
            return this.items.remove(0);
        }
    }
    @Override
    public String toString(){
        return items.toString();
    }
}

测试代码:

public class Helloworld{
    public static void main(String[] args) {
        Queue<String> genericQueue = new Queue<String>();
        genericQueue.queue("A");
        genericQueue.queue("C");
        genericQueue.queue("B");
        genericQueue.queue("D");
        //genericQueue.queue(1);//编译错误
//        打印队列
        System.out.println(genericQueue);
//        队列出列
        genericQueue.dequeue();
        //        打印队列
        System.out.println(genericQueue);
    }
}

运行结果:

[A, C, B, D]
[C, B, D]


三、自定义泛型接口与泛型方法


(1)自定义泛型接口

自定义泛型接口与自定义泛型类类似,定义的方式完全一样。

package 泛型.测试自定义泛型队列;
//自定义泛型接口
/**
 * 自定义的泛型队列集合
 * */
public interface IQueue<T> {
/**
 * 入队方法
 * @param item 参数需要入队的元素 */
    public void queue(T item);
    /**
     * 出队方法
     * @return 返回出队元素 */
    public T dequeue();
}

实现这接口IQueue具体方式有很多,可以是List、Set或Hash等多种不同方式,下面笔者给出一个基 于List实现方式,代码如下:

package 泛型.测试自定义泛型队列;
import java.util.ArrayList;
import java.util.List;
/**
 * 自定义的泛型队列集合
 * */
public class ListQueue<T> implements IQueue<T>  {
//    声明保存队列元素集合items
    private List<T> items;
//    构造方法初始化集合items
    public ListQueue(){
        this.items = new ArrayList<T>();
    }
    /**
     * 入队方法
     *
     * @param item
     * 参数需要入队的元素 */
    @Override
    public void queue(T item) {
        this.items.add(item);
    }
    /**
     * 出队方法
     *
     * @return 返回出队元素 */
    @Override
    public T dequeue() {
        if (items.isEmpty()) {
            return null;
        } else {
            return this.items.remove(0);
        }
    }
    @Override
    public String toString(){
        return items.toString();
    }
}

测试代码:

package 泛型.测试自定义泛型队列;
//测试自定义泛型队列
public class hello {
    public static void main(String[] args) {
        ListQueue<String> genericQueue = new ListQueue<String>();
        genericQueue.queue("A");
        genericQueue.queue("C");
        genericQueue.queue("B");
        genericQueue.queue("D");
        //genericQueue.queue(1);//编译错误
//        打印队列
        System.out.println(genericQueue);
//        队列出列
        genericQueue.dequeue();
        //        打印队列
        System.out.println(genericQueue);
    }
}

运行结果:

[A, C, B, D]
[C, B, D]


(2)泛型方法

在方法中也可以使用泛型,即方法的参数类型或返回值类型,可以用类型参数表示。

泛型的类型参数也可以限定一个边界,例如比较方法isEquals()只想用于数值对象大小的比较, 实现代码如下:

package 泛型.泛型方法;
//泛型方法
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println(isEquals(new Integer(1),new Integer(5)));
//        发生了自动封装
        System.out.println(isEquals(1,5));
        System.out.println(isEquals(new Double(1.0),new Double(1.0)));
        //        发生了自动封装
        System.out.println(isEquals(1.0,1.0));
//        如果不注释以下代码就会报错,因为下面的限定是Number类型
//        System.out.println(isEquals("A","A"));
    }
//        限定参数类型为Number
//    如果不加extends Number 即他们是任何引用类型,返回值是<T>
    public static <T extends Number> boolean isEquals(T a,T b){ //2
        return a.equals(b);
    }
}

上述代码第2行定义泛型使用语句,该语句是限定类型参数只能是Number类型。 所以代码第1行的试图传递String类型的参数,则会发生编译错误。


相关文章
|
13天前
|
JavaScript Java 编译器
Java包装类和泛型的知识点详解
Java包装类和泛型的知识点的深度理解
|
1月前
|
Java
java中的泛型类型擦除
java中的泛型类型擦除
13 2
|
1月前
|
存储 Java fastjson
Java泛型-4(类型擦除后如何获取泛型参数)
Java泛型-4(类型擦除后如何获取泛型参数)
33 1
|
12天前
|
存储 监控 安全
泛型魔法:解码Java中的类型参数
泛型魔法:解码Java中的类型参数
33 0
泛型魔法:解码Java中的类型参数
|
14天前
|
Java API
Java基础—笔记—内部类、枚举、泛型篇
本文介绍了Java编程中的内部类、枚举和泛型概念。匿名内部类用于简化类的创建,常作为方法参数,其原理是生成一个隐含的子类。枚举用于表示有限的固定数量的值,常用于系统配置或switch语句中。泛型则用来在编译时增强类型安全性,接收特定数据类型,包括泛型类、泛型接口和泛型方法。
9 0
|
1月前
|
存储 安全 Java
JAVA泛型
JAVA泛型
12 0
|
1月前
|
Java 编译器
[java进阶]——泛型类、泛型方法、泛型接口、泛型的通配符
[java进阶]——泛型类、泛型方法、泛型接口、泛型的通配符
|
1月前
|
存储 Java 编译器
Java——泛型
Java——泛型
16 0
|
1月前
|
存储 安全 Java
JAVA的泛型
JAVA的泛型
9 0
|
2月前
|
算法 Java 编译器
重学Java之泛型的基本使用
重学Java之泛型的基本使用