MIDAS GTS NX三维顶管下穿桥梁仿真分析全流程指南
2026/8/1 11:30:49
本篇我们来讲解 Java 泛型~
ArrayList,HashMap)默认存储Object类型。当你从集合中取出元素时,需要强制转型(Casting)为你期望的类型。如果实际存储的类型与你强制转型的目标类型不匹配,就会在运行时抛出ClassCastException。泛型在编译时就检查类型是否匹配,大大减少了这类运行时错误。ArrayList list = new ArrayList(); // 存储 Object list.add("Hello"); list.add(123); // 不小心存入了 Integer String str = (String) list.get(1); // 编译通过,但运行时抛出 ClassCastException!ArrayList<String> list = new ArrayList<>(); // 指定存储 String list.add("Hello"); // list.add(123); // 编译错误!编译器阻止存入 Integer String str = list.get(0); // 无需强制转型,直接是 StringList<T>接口可以用于存放任何类型T的元素,而不需要为String、Integer等分别编写StringList、IntegerList。<>声明一个或多个类型参数。这些参数可以在类体中像普通类型一样使用(作为字段类型、方法参数类型、方法返回类型等)。public class ClassName<T1, T2, ..., Tn> { // 类体可以使用 T1, T2, ..., Tn }<>中指定具体的类型参数(称为类型实参,Type Argument)。ClassName<具体类型1, 具体类型2, ..., 具体类型n> obj = new ClassName<>();Box类public class Box<T> { private T content; // T 表示某种类型的内容 public void setContent(T content) { this.content = content; } public T getContent() { return content; } }Box<String> stringBox = new Box<>(); // T 被指定为 String stringBox.setContent("Hello Generics!"); String message = stringBox.getContent(); // 直接是 String,无需转型 Box<Integer> intBox = new Box<>(); // T 被指定为 Integer intBox.setContent(42); int number = intBox.getContent(); // 自动拆箱为 intPair<K, V>。T、E、K、V),但这只是约定俗成。<>称为菱形语法(Diamond Operator),允许省略类型实参(编译器会根据声明推断)。public interface InterfaceName<T1, T2, ..., Tn> { // 接口方法可以使用 T1, T2, ..., Tn }public interface Producer<T> { T produce(); } public class StringProducer implements Producer<String> { @Override public String produce() { return "Generated String"; } }public class GenericProducer<T> implements Producer<T> { @Override public T produce() { // ... 生产 T 类型对象的逻辑 ... return result; } }GenericProducer<Integer> intProducer = new GenericProducer<>();<>声明类型参数。public <T1, T2, ..., Tn> 返回类型 方法名(参数列表) { // 方法体可以使用 T1, T2, ..., Tn }public class Util { // 泛型方法:交换数组中两个元素的位置 public static <T> void swap(T[] array, int i, int j) { T temp = array[i]; array[i] = array[j]; array[j] = temp; } // 泛型方法:查找数组中最大值 (要求 T 实现了 Comparable<T>) public static <T extends Comparable<T>> T max(T[] array) { if (array == null || array.length == 0) return null; T maxVal = array[0]; for (T element : array) { if (element.compareTo(maxVal) > 0) { maxVal = element; } } return maxVal; } }Integer[] intArray = {1, 5, 3, 2}; Util.swap(intArray, 1, 2); // <Integer> 被编译器推断出来 Integer maxInt = Util.max(intArray); // 同样推断出 <Integer> String[] strArray = {"apple", "banana", "cherry"}; String maxStr = Util.max(strArray); // 推断出 <String><T extends Comparable<T>>是类型边界(Type Bound),用于约束T必须是实现了Comparable<T>接口的类型,这样方法体内才能安全地调用compareTo方法。我们稍后会详细讲解边界。List<String>的是String)。Object),并在必要的地方插入强制转型。// 源代码 (编译前) List<String> list = new ArrayList<>(); list.add("Hi"); String s = list.get(0); // 经过类型擦除后的等效代码 (编译后,近似表示) List list = new ArrayList(); // 类型参数 <String> 被擦除 list.add("Hi"); // 添加 String 没问题 String s = (String) list.get(0); // 编译器插入的强制转型List<String>.class或new T()都是不合法的,因为运行时T已经不存在了(被擦除为Object或边界类型)。new List<String>[10]通常会导致编译警告或错误,因为数组需要确切知道其元素类型,而擦除后List<String>和List<Integer>在运行时都是List,数组无法区分。Box<String>.class == Box<Integer>.class结果为true。?<?>:表示任何类型。Object或某个公共父类使用),而不关心具体类型时。public static void printList(List<?> list) { for (Object obj : list) { // 元素被当作 Object 处理 System.out.println(obj); } // list.add(new Object()); // 错误!不能添加 (除了 null),因为不知道具体类型 }List<?>的变量添加除null以外的任何元素(因为你不知道里面具体是什么类型)。<? extends UpperBound>:表示UpperBound类型或其子类型。UpperBound类型),但通常不能添加元素(除了null)。public static double sumOfList(List<? extends Number> list) { double sum = 0.0; for (Number num : list) { // 安全读取,每个元素都是 Number 或其子类 sum += num.doubleValue(); } return sum; // list.add(new Integer(1)); // 错误!不能添加,可能是 List<Double> } List<Integer> intList = Arrays.asList(1, 2, 3); List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3); sumOfList(intList); // OK, Integer extends Number sumOfList(doubleList); // OK, Double extends Number<? super LowerBound>:表示LowerBound类型或其父类型。LowerBound或其子类),但读取时只能当作Object(因为不知道具体父类是什么)。public static void addNumbers(List<? super Integer> list) { for (int i = 1; i <= 5; i++) { list.add(i); // 安全写入,Integer 是 LowerBound } // Integer num = list.get(0); // 错误!读取出来可能是 Number 或 Object Object obj = list.get(0); // 只能当作 Object 读取 } List<Number> numList = new ArrayList<>(); List<Object> objList = new ArrayList<>(); addNumbers(numList); // OK, Number super Integer addNumbers(objList); // OK, Object super IntegerProducer),使用<? extends T>。Consumer),使用<? super T>。T。extends关键字(在泛型中,extends可以表示类继承或接口实现)。<T extends ClassOrInterface><T extends ClassA & InterfaceB & InterfaceC>(类只能有一个且必须在第一个,接口可以有多个)。// T 必须是 Number 或其子类 public class NumericBox<T extends Number> { private T value; // ... getter, setter ... public double getValueAsDouble() { return value.doubleValue(); // 安全调用,因为 T 是 Number } } // T 必须实现 Comparable 接口,并且能够和自己比较 (Comparable<T>) public static <T extends Comparable<T>> T max(T a, T b) { return (a.compareTo(b) > 0) ? a : b; } // 多个边界:T 必须是 Serializable 的子类 且 实现 Comparable public class SerializableComparable<T extends Serializable & Comparable<T>> { // ... }Object如果没有边界)。Box<Number>和Box<Integer>没有继承关系。即使Integer是Number的子类,Box<Integer>也不是Box<Number>的子类。List<? extends Number>是List<?>的子类型。List<Number>是List<? super Number>的子类型? (不是直接的父子关系,但List<Number>可以赋值给List<? super Number>变量)List<Integer>可以赋值给List<? extends Number>变量(因为Integer extends Number)。List<Number>可以赋值给List<? super Integer>变量(因为Number super Integer)。new T()是非法的,因为运行时T被擦除。Class<T>clazz 参数)或工厂模式。public class Box<T> { // private static T staticField; // 错误! // public static T staticMethod() { ... } // 错误! public static <U> U genericStaticMethod(U u) { ... } // OK,泛型方法有自己的类型参数 }List<int>,只能用List<Integer>。自动装箱/拆箱缓解了这个问题。catch (T e)是不允许的。泛型类也不能直接或间接继承Throwable。public class Example { public void print(List<String> list) { ... } public void print(List<Integer> list) { ... } // 编译错误!擦除后都是 print(List) }泛型通过类型参数、类型擦除、通配符和类型边界等机制,提供了强大的类型安全性和代码复用能力。理解类型擦除是深入掌握泛型行为的关键,而通配符(尤其是extends和super)则提供了处理不同类型集合时的灵活性。遵循 PECS 原则有助于正确使用通配符。虽然泛型有一些限制(主要是由类型擦除带来的),但它们极大地提升了 Java 程序的健壮性和可读性。