ZetCode

Java Collections.checkedCollection 方法

上次修改时间:2025 年 4 月 20 日

Collections.checkedCollection 方法为 Java 集合提供运行时类型安全。它返回指定集合的动态类型安全视图。任何尝试插入错误类型元素的操作都会导致立即抛出 ClassCastException 异常。

当处理不使用泛型的遗留代码或 API 时,此方法特别有用。它有助于在插入点捕获类型错误,而不是稍后在元素访问期间捕获。checked collection 包装器在运行时强制执行类型约束。

Collections.checkedCollection 概述

checkedCollection 方法在 Java 5 中引入,以解决泛型的类型安全问题。它包装现有的集合,并对所有插入操作执行运行时类型检查。原始集合仍然可以通过包装器访问。

方法签名是 static <E> Collection<E> checkedCollection(Collection<E> c, Class<E> type)。它接受要包装的集合和表示元素类型的 Class 对象。对返回集合的所有修改都会反映在原始集合中。

checkedCollection 的基本用法

此示例演示了 Collections.checkedCollection 的基本用法。我们创建一个常规 ArrayList,然后用类型检查版本包装它。该示例显示了有效和无效的操作。

BasicCheckedCollection.java
package com.zetcode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

public class BasicCheckedCollection {

    public static void main(String[] args) {
        
        Collection<String> names = new ArrayList<>();
        names.add("John");
        names.add("Alice");
        
        // Create type-safe checked collection
        Collection<String> checkedNames = 
            Collections.checkedCollection(names, String.class);
        
        // Valid operation
        checkedNames.add("Bob");
        System.out.println("Valid add: " + checkedNames);
        
        try {
            // Invalid operation - adding wrong type
            Collection rawCollection = checkedNames;
            rawCollection.add(42); // Should throw ClassCastException
        } catch (ClassCastException e) {
            System.out.println("Caught exception: " + e.getMessage());
        }
    }
}

此代码首先创建一个常规的 String ArrayList。然后,我们用 checkedCollection 包装它,指定 String.class 作为元素类型。包装器允许添加有效的 String,但拒绝无效类型。

该示例演示了 checked collection 如何立即捕获类型不匹配。如果没有包装器,错误可能会被忽略直到很久以后,从而使调试更加困难。

使用遗留代码

当与不使用泛型的遗留代码交互时,checkedCollection 特别有用。此示例演示了如何使用运行时类型检查安全地将非泛型代码与泛型集合集成。

LegacyCodeIntegration.java
package com.zetcode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

public class LegacyCodeIntegration {

    public static void main(String[] args) {
        
        // Modern generic collection
        Collection<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        
        // Wrap with checked collection
        Collection<Integer> checkedNumbers = 
            Collections.checkedCollection(numbers, Integer.class);
        
        // Pass to legacy method
        processLegacyCollection(checkedNumbers);
        
        System.out.println("After legacy processing: " + numbers);
    }
    
    // Legacy method without generics
    private static void processLegacyCollection(Collection rawCollection) {
        rawCollection.add(3); // Valid
        try {
            rawCollection.add("Four"); // Invalid
        } catch (ClassCastException e) {
            System.out.println("Legacy code caught: " + e.getMessage());
        }
    }
}

此示例演示了当将泛型集合传递给遗留代码时,checkedCollection 如何保护它们。即使接收代码不使用泛型,包装器也能确保类型安全。遗留方法仍然可以添加有效的元素,但会立即获得有关无效元素的反馈。

输出显示,有效的添加操作成功,而无效的添加操作被捕获。这种方法有助于逐步更新遗留代码,同时保持类型安全。

带有自定义对象的 Checked Collection

checkedCollection 同样适用于自定义对象和内置类型。此示例演示了如何将其与自定义 Person 类一起使用,以确保只能将 Person 对象添加到集合中。

CustomObjectCheckedCollection.java
package com.zetcode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

class Person {
    private String name;
    
    public Person(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "Person{" + "name=" + name + '}';
    }
}

public class CustomObjectCheckedCollection {

    public static void main(String[] args) {
        
        Collection<Person> people = new ArrayList<>();
        people.add(new Person("John"));
        
        Collection<Person> checkedPeople = 
            Collections.checkedCollection(people, Person.class);
        
        // Valid addition
        checkedPeople.add(new Person("Alice"));
        System.out.println("Valid additions: " + checkedPeople);
        
        try {
            // Invalid addition
            checkedPeople.add("Not a person");
        } catch (ClassCastException e) {
            System.out.println("Caught invalid addition: " + e.getMessage());
        }
    }
}

此示例创建一个自定义 Person 类和一个用于保存 Person 对象的集合。我们用 checkedCollection 包装集合,以确保只能添加 Person 对象。包装器成功捕获了添加不兼容类型的尝试。

输出显示,有效的 Person 对象被正常添加,而无效类型会触发立即异常。这种强制执行在运行时发生,提供了强大的类型安全保证。

性能注意事项

虽然 checkedCollection 提供了有价值的类型安全,但它确实会产生少量性能开销。此示例演示了如何衡量影响以及何时这种权衡是值得的。

CheckedCollectionPerformance.java
package com.zetcode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

public class CheckedCollectionPerformance {

    private static final int ITERATIONS = 100000;

    public static void main(String[] args) {
        
        Collection<Integer> regular = new ArrayList<>();
        Collection<Integer> checked = 
            Collections.checkedCollection(new ArrayList<>(), Integer.class);
        
        // Test regular collection
        long start = System.currentTimeMillis();
        for (int i = 0; i < ITERATIONS; i++) {
            regular.add(i);
        }
        long regularTime = System.currentTimeMillis() - start;
        
        // Test checked collection
        start = System.currentTimeMillis();
        for (int i = 0; i < ITERATIONS; i++) {
            checked.add(i);
        }
        long checkedTime = System.currentTimeMillis() - start;
        
        System.out.println("Regular collection time: " + regularTime + "ms");
        System.out.println("Checked collection time: " + checkedTime + "ms");
        System.out.println("Overhead: " + 
            (100.0 * (checkedTime - regularTime) / regularTime) + "%");
    }
}

此性能测试比较了常规集合与 checked collection 上的操作。我们对两者执行相同数量的添加操作,并测量时间差。结果显示了运行时类型检查的开销。

虽然开销是可测量的,但与早期类型错误检测的好处相比,它通常可以忽略不计。确切的影响取决于用例和集合大小。对于大多数应用程序,安全优势超过了小的性能成本。

与其他集合包装器结合使用

checkedCollection 可以与其他集合包装器(如 unmodifiableCollection)结合使用,以实现全面的保护。此示例演示了如何创建一个既类型安全又不可变的集合。

CombinedWrappers.java
package com.zetcode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

public class CombinedWrappers {

    public static void main(String[] args) {
        
        Collection<Double> temperatures = new ArrayList<>();
        temperatures.add(23.5);
        temperatures.add(18.2);
        
        // Create checked and unmodifiable view
        Collection<Double> safeTemps = Collections.unmodifiableCollection(
            Collections.checkedCollection(temperatures, Double.class));
        
        System.out.println("Initial collection: " + safeTemps);
        
        try {
            // Attempt type violation
            Collection raw = safeTemps;
            raw.add("Hot");
        } catch (ClassCastException e) {
            System.out.println("Caught type violation: " + e.getMessage());
        }
        
        try {
            // Attempt modification
            safeTemps.add(25.0);
        } catch (UnsupportedOperationException e) {
            System.out.println("Caught modification attempt: " + e.getMessage());
        }
    }
}

此示例创建一个集合,该集合受到类型违规和修改的保护。我们首先使用 checkedCollection 进行类型安全,然后使用 unmodifiableCollection 防止更改。结果是原始集合的完全受保护视图。

输出表明,类型违规和修改尝试都会立即被捕获。当提供对集合的只读访问权限同时保持类型安全时,此组合非常有用。

真实世界的用例:API 边界保护

checkedCollection 的一个实际用途是保护 API 边界。此示例演示了如何在接受来自外部代码的集合时使用它,以确保它们仅包含预期的类型。

ApiBoundaryProtection.java
package com.zetcode;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class ApiBoundaryProtection {

    public static void main(String[] args) {
        
        // Simulate external input
        Set rawInput = new HashSet();
        rawInput.add("Valid");
        rawInput.add(42); // Oops, wrong type
        
        try {
            processUserData(rawInput);
        } catch (ClassCastException e) {
            System.out.println("API rejected invalid input: " + e.getMessage());
        }
    }
    
    public static void processUserData(Collection<String> userData) {
        // Wrap with checked collection at API boundary
        Collection<String> safeData = 
            Collections.checkedCollection(userData, String.class);
        
        System.out.println("Processing data: " + safeData);
        // Process the data...
    }
}

此示例模拟一个 API 方法,该方法接受来自外部代码的 String 集合。通过在 API 边界用 checkedCollection 包装输入,我们确保立即检测到任何无效元素。包装器捕获了错误地添加到应该是 String 集合中的 Integer。

输出显示 API 正确地拒绝了无效输入。这种模式在公共 API 中尤其有价值,因为您无法控制所有调用代码,但需要在内部保持类型安全。

来源

Java Collections.checkedCollection 文档

在本教程中,我们深入探讨了 Collections.checkedCollection 方法。我们介绍了基本用法、遗留代码集成、性能和实际应用。此实用程序对于在 Java 集合中保持运行时类型安全非常有价值。

作者

我叫 Jan Bodnar,是一位经验丰富的敬业程序员。我于 2007 年开始撰写编程文章,此后撰写了 1,400 多篇文章和八本电子书。凭借超过八年的教学经验,我致力于分享我的知识并帮助其他人掌握编程概念。

列出所有Java教程