回归java9-java进阶-泛型

文章目录

  • 泛型
    • 使用泛型
    • 自定义泛型类
    • 自定义泛型接口
    • 泛型方法

泛型

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

// 先看一个使用集合的实例

List list = new ArrayList();

// 向集合中添加元素
list.add("1");
list.add("2");
list.add("3");

// 遍历集合
for (Object item : list) {
    Integer element = (Integer) item; // 强制类型转换
    System.out.println("读取集合元素:" + element);
}

上述,将数据保存到集合中再取出。但对java5之前:放入一种特定类型,但是取出时全部是Object类型,在具体使用时需要将元素转换为特定类型。
强制类型转换是有风险的,如果不进行判断就臆断进行类型转换,会发生ClassCastException异常。
上述有效代码的最后一行,试图将java.lang.String对象转换为java.lang.Integer对象。
在java5之前没有好的解决办法,在类型转换之前要通过instanceof运算符判断该对象是否是目标类型。
而泛型的引入可以将这些运行时异常提前到编译期暴露出来,这增强了类型安全检查。

// 修改代码

List<String> list = new ArrayList<String>();

list.add("1");
list.add("2");
list.add("3");
// list.add(new Date); // 发生编译错误

for (String item : list) {
    // Integer element = (Integer) item;
    System.out.println("读取集合元素:" + item);
}

这就是List和ArrayList的泛型表示方式,尖括号中可以是任何引用类型,它限定了集合中是否能存放该种类型的对象,所以试图添加非String类型元素时,会发生编译错误。
增强for循环遍历,从集合中取出的元素就是String类型,再试图转换为Integer就会发生编译错误。
(原本在运行时发生的异常,提早暴露到编译期,使程序员及早发现问题,避免程序发布上线之后发生系统崩溃的情况)

在集合中如果没有使用泛型,Eclipse等IDE工具都会警告。可以单击警告,根据Eclipse的修订功能,修订这些警告。在弹出的对话框中选择“推断通用类型参数”就可以添加泛型。

使用泛型

java5后所有集合类型都可以有泛型类型。

// Set泛型集合示例

Set<String> set = new HashSet<String>();

set.add("A");
set.add("D");
set.add("E");

for (String item : set) {
    System.out.println(item);
}

Iterator<String> it = set.iterator();
while (it.hasNext()) {
    String item = it.next();
    System.out.println(item);
}
// Map泛型集合示例

Map<Integer, String> map = new HashMap<Integer, String>();

map.put(102, "张三");
map.put(105, "李四");

Set<Integer> keys = map.keySet();
for (Integer key : keys) {
    String value = map.get(key);
    System.out.println("key=%d - value=%s \n", key, value);
}

Collection<String> values = map.values();
Itetator<String> it = values.itetator();
while (it.hasNext()) {
    String item = it.next();
    System.out.println(item);
}

自定义泛型类

根据自己的需要可以自定义泛型类、泛型接口、带有泛型参数的方法。

数据结构 - 队列(queue),先入先出(FIFO)规则。
java SE提供支持泛型的队列java.util.Queue类型。
自己实现的支持泛型的队列集合:

import java.util.ArrayList;
import java.util.List;

/**
 * 自定义的泛型队列集合
 */
// 定义Queue泛型类型的队列,T是参数类型占位符
public class Queue<T> {
    
    // 声明List泛型集合成员变量items,用来保存队列中的元素
    private List<T> items;
    
    // 构造方法,初始化集合items
    public Queue() {
        this.items = new ArrayList<T>();
    }
    
    /**
     * 入队方法
     * @param item 参数需要入队的元素
     */
    public void queue(T item) { // 这里的占位符与Queue中的占位符保持一致
        this.items.add(item);
    }
    
    /**
     * 出队方法
     * @return 返回出队元素
     */
    public T dequeue() { // 这里的占位符与Queue中的占位符保持一致
        if (items.isEmpty()) { // 集合是否有元素
            return null;
        } else {
            return this.items.remove(0); // 删除队列的第一个元素,并将删除的元素返回
        }
    }
    
    @Override
    public String toString() {
        return items.toString();
    }
}

泛型中参数类型占位符可以使任何大写或小写的英文字母,一般情况下习惯使用字母T、E、K、U等大写英文字母。

调用队列示例:

Queue<String> genericQueue = new Queue<String>();
genericQueue.queue("A");
genericQueue.queue("C");
genericQueue.queue("B");
// genericQueue.queue(1); // 编译错误
System.out.println(genericQueue); // [A, C, B]
genericQueue.dequeue();
System.out.println(genericQueue); // [C, B]

Map,注意程序中哪些地方是用K表示,哪些地方用V表示。

自定义泛型接口

与自定义泛型类类似。

将上面示例修改为队列接口:

public interface IQueue<T> {
    
    /**
     * 入队方法
     * @param item 参数需要入队的元素
     */
    public void queue(T item);
    
    /**
     * 出队方法
     * @return 返回出队元素
     */
    public T dequeue();
}

实现接口IQueue的具体方式有很多(List、Set、Hash等),下面是基于List的:

import java.util.ArrayList;
import java.util.List;

/**
 * 自定义的泛型队列集合
 */
// 定义Queue泛型类型的队列,T是参数类型占位符
public class ListQueue<T> implements IQueue<T> {
    
    // 声明List泛型集合成员变量items,用来保存队列中的元素
    private List<T> items;
    
    // 构造方法,初始化集合items
    public ListQueue() {
        this.items = new ArrayList<T>();
    }
    
    /**
     * 入队方法
     * @param item 参数需要入队的元素
     */
    @Override
    public void queue(T item) { // 这里的占位符与Queue中的占位符保持一致
        this.items.add(item);
    }
    
    /**
     * 出队方法
     * @return 返回出队元素
     */
    @Override
    public T dequeue() { // 这里的占位符与Queue中的占位符保持一致
        if (items.isEmpty()) { // 集合是否有元素
            return null;
        } else {
            return this.items.remove(0); // 删除队列的第一个元素,并将删除的元素返回
        }
    }
    
    @Override
    public String toString() {
        return items.toString();
    }
}

实现泛型接口的具体类也应该支持泛型,所以Queue中类型参数名要与IQueue接口中的类型参数名一致,占位符所用字母相同。

泛型方法

方法的参数类型或返回值类型可以用类型参数表示。

public static void main(String[] args) {
    
    isEquals(new Integer(1), new Integer(5));
    isEquals(1, 5); // 发生了自动装箱
    isEquals(new Double(1.0), new Double(1.0));
    isEquals("A", "A");
}

public static <T> boolean isEquals(T a, T b) {
    return a.equals(b);
}

泛型的类型参数可以设定边界。
比较方法只想用于数值对象大小的比较:

// 限定类型参数为Number
public static <T extends Number> boolean isEquals(T a, T b) {
    return a.equals(b);
}

你可能感兴趣的:(java,java)