本篇总结涵盖了 Java 中基本数据类型与引用数据类型的区别、泛型为何不能使用基本数据类型声明、泛型上下限的使用以及 Java 原生 API 中泛型的经典应用场景。每个知识点都配有详细的解释和代码示例,便于理解和复习。
byte、short、int、longfloat、doublecharbooleanint:4 字节double:8 字节int:0boolean:falsenull,表示不引用任何对象。null。null。Java 的泛型是通过 类型擦除(Type Erasure)实现的,编译时会将泛型参数擦除为其上限类型(默认为 Object)。由于基本数据类型不是对象类型,不能被替换为 Object,因此泛型无法直接使用基本数据类型。
java自动换行:关放大阅读展开代码List<Integer> list = new ArrayList<>(); // 编译后类型擦除为: List list = new ArrayList<>();
基本数据类型不是对象,不能作为泛型的类型参数。泛型要求类型参数是一个对象类型(引用类型)。
Java 提供了 自动装箱(Autoboxing)和 自动拆箱(Unboxing)机制,将基本数据类型与其对应的包装类互相转换,使得基本数据类型可以间接用于泛型。
int ↔ Integerdouble ↔ Doublechar ↔ Characterboolean ↔ Boolean
示例:plaintext自动换行:关放大阅读展开代码java复制代码List<Integer> intList = new ArrayList<>(); intList.add(10); // 自动装箱,将 int 类型的 10 转换为 Integer 对象 int num = intList.get(0); // 自动拆箱,将 Integer 转换为 int
泛型上下限通过通配符 ? extends T(上限)和 ? super T(下限)来限定类型参数的范围,提高代码的灵活性和类型安全性。
? extends T? extends T 表示类型是 T 或 T 的子类,主要用于 读取,不能 写入(除了 null)。
示例:
java自动换行:关放大阅读展开代码import java.util.List; import java.util.ArrayList; public class ExtendsExample { public static void main(String[] args) { List<? extends Number> list = new ArrayList<Integer>(); // list.add(1); // 编译错误,不能添加元素 Number num = list.get(0); // 可以读取,类型为 Number } }
解释:
List<? extends Number> 表示列表中的元素类型是 Number 或其子类(如 Integer、Double)。Number。? super T? super T 表示类型是 T 或 T 的父类,允许写入 T 及其子类的对象,适合 写入。
示例:
java自动换行:关放大阅读展开代码import java.util.List; import java.util.ArrayList; public class SuperExample { public static void main(String[] args) { List<? super Integer> list = new ArrayList<Number>(); list.add(1); // 可以添加 Integer list.add(new Integer(2)); // 可以添加 Integer // list.add(new Object()); // 编译错误,不能添加非 Integer 子类 Object obj = list.get(0); // 读取时只能作为 Object 类型 } }
解释:
List<? super Integer> 表示列表中的元素类型是 Integer 或其父类(如 Number、Object)。Integer 及其子类对象,因为它们都可以向上转型为 Integer 的父类。Object 类型,因为无法确定具体类型。
多态性解释:Integer 类型的对象可以存储到 Number 或 Object 类型的列表中。?? 表示任意类型,由于类型未知,既不能安全地进行 读取,也不能 写入(除了 null)。
示例:
java自动换行:关放大阅读展开代码java复制代码import java.util.List; import java.util.ArrayList; public class UnboundedExample { public static void main(String[] args) { List<?> list = new ArrayList<String>(); // list.add("hello"); // 编译错误,不能添加元素 Object obj = list.get(0); // 可以读取,类型为 Object } }
解释:
List<?> 表示列表中的元素类型未知,可以是任何类型。Java 的泛型在标准库中广泛应用,以下是一些经典的使用泛型的地方。
List 接口及其实现类(如 ArrayList、LinkedList)使用泛型,确保列表中的元素类型一致。
java自动换行:关放大阅读展开代码java复制代码List<String> stringList = new ArrayList<>(); stringList.add("Hello"); // stringList.add(1); // 编译错误,不能添加非 String 类型
Map<K, V> 是键值对集合,使用泛型参数 K 和 V 表示键和值的类型。
java自动换行:关放大阅读展开代码java复制代码Map<Integer, String> map = new HashMap<>(); map.put(1, "One"); // map.put("Two", 2); // 编译错误,键必须是 Integer,值必须是 String
Set 接口及其实现类(如 HashSet、TreeSet)使用泛型,确保集合中的元素类型一致。
plaintext自动换行:关放大阅读展开代码java复制代码Set<Double> set = new HashSet<>(); set.add(3.14); // set.add("Pi"); // 编译错误,不能添加非 Double 类型
Queue 接口及其实现类(如 LinkedList、PriorityQueue)使用泛型,指定队列中元素的类型。
plaintext自动换行:关放大阅读展开代码java复制代码Queue<Integer> queue = new LinkedList<>(); queue.add(1); // queue.add("String"); // 编译错误,不能添加非 Integer 类型
Comparator<T> 接口Comparator 接口用于比较对象,泛型参数 T 确保类型一致。
plaintext自动换行:关放大阅读展开代码java复制代码Comparator<String> comparator = new Comparator<String>() { @Override public int compare(String s1, String s2) { return s1.length() - s2.length(); } }; List<String> strings = Arrays.asList("apple", "banana", "cherry"); Collections.sort(strings, comparator);
Optional<T> 类Optional 是一个容器对象,用于表示可能存在或不存在的值,避免 NullPointerException。
plaintext自动换行:关放大阅读展开代码java复制代码Optional<String> optionalString = Optional.of("Hello"); optionalString.ifPresent(System.out::println); // 输出 "Hello" Optional<Integer> optionalInteger = Optional.ofNullable(null); Integer value = optionalInteger.orElse(0); // 如果为空,返回默认值 0
Stream<T> APIStream API 使用泛型,支持对集合的函数式操作。
plaintext自动换行:关放大阅读展开代码java复制代码List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); List<String> filteredNames = names.stream() .filter(name -> name.startsWith("A")) .collect(Collectors.toList());
Future<T> 和 CompletableFuture<T>在并发编程中,Future 和 CompletableFuture 使用泛型表示异步计算的结果类型。
plaintext自动换行:关放大阅读展开代码java复制代码ExecutorService executorService = Executors.newFixedThreadPool(2); Future<Integer> future = executorService.submit(() -> { // 执行耗时任务 return 42; }); CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello"); completableFuture.thenAccept(result -> System.out.println(result)); // 输出 "Hello"
Class<T> 类Class<T> 使用泛型表示类的类型信息,常用于反射机制。
plaintext自动换行:关放大阅读展开代码java复制代码Class<String> stringClass = String.class; String str = stringClass.newInstance(); // 创建新的 String 实例
Java 8 引入的函数式接口广泛使用泛型。
Supplier<T>提供一个类型 T 的值。
plaintext自动换行:关放大阅读展开代码java复制代码Supplier<String> supplier = () -> "Hello"; String result = supplier.get(); // 返回 "Hello"
Consumer<T>消费一个类型 T 的值。
plaintext自动换行:关放大阅读展开代码java复制代码Consumer<String> consumer = s -> System.out.println(s); consumer.accept("Hello"); // 输出 "Hello"
Function<T, R>将类型 T 的值映射为类型 R 的值。
plaintext自动换行:关放大阅读展开代码java复制代码Function<String, Integer> function = s -> s.length(); Integer length = function.apply("Hello"); // 返回 5
Java 中基本数据类型和引用数据类型在存储方式、默认值、操作方式等方面存在显著区别。由于泛型的类型擦除机制,泛型无法直接使用基本数据类型,但可以通过包装类和自动装箱/拆箱机制间接地在泛型中使用基本数据类型。 泛型上下限的使用可以提高代码的灵活性和类型安全性。在 Java 原生 API 中,泛型被广泛应用于集合框架、并发编程、函数式编程等方面,提高了代码的可读性和可维护性。
本文作者:hedeoer
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!