做视频搬运工的网站,河南网站建设哪家好,精通网站建设 100,做网站是学什么编程语言泛型#xff08;Generics#xff09;是Java编程语言中的一个强大的特性#xff0c;它提供了编译时类型安全检测机制#xff0c;这意味着可以在编译期间检测到非法的类型。泛型的使用减少了程序中的强制类型转换和运行时错误的可能性。
一、泛型使用规范
类型参数命名约定…泛型Generics是Java编程语言中的一个强大的特性它提供了编译时类型安全检测机制这意味着可以在编译期间检测到非法的类型。泛型的使用减少了程序中的强制类型转换和运行时错误的可能性。
一、泛型使用规范
类型参数命名约定通常使用单个大写字母来表示类型参数例如 E 表示集合的元素类型K 和 V 分别代表键和值的类型T 通常代表类型Type。泛型类和接口在定义泛型类或接口时你应该在类名后面加上尖括号并在其中声明类型参数。例如class BoxT。泛型方法在方法返回类型之前声明类型参数以使该方法成为泛型方法。如 public T void method(T param)。类型通配符使用 ? 表示未知类型。? extends T 表示接受 T 或其子类上界通配符? super T 表示接受 T 或其父类下界通配符。类型擦除泛型信息仅在编译阶段存在在进入 JVM 前与泛型有关的类型信息就会被擦除这个过程称为类型擦除。类型擦除是为了兼容旧版本的Java。避免创建泛型数组Java 不允许实例化泛型数组如 new List[] 是非法的。可以通过创建类型为 List[] 的数组然后将其转换为 ListT[] 来绕过这个限制但这种做法不安全并会引发警告。限制由于类型擦除某些操作在泛型中是不允许的比如判断 (instanceof) 泛型类型、创建泛型实例 (new T())、创建泛型的数组。
二、泛型的基本用法
1、泛型类
public class BoxT {private T t;public void set(T t) {this.t t;}public T get() {return t;}
}// 实例化一个泛型类
BoxInteger integerBox new Box();
integerBox.set(10);BoxString stringBox new Box();
stringBox.set(Hello World);2、泛型接口
// 定义一个泛型接口
public interface PairK, V {public K getKey();public V getValue();
}// 实现泛型接口
public class OrderedPairK, V implements PairK, V {private K key;private V value;public OrderedPair(K key, V value) {this.key key;this.value value;}public K getKey() {return key;}public V getValue() {return value;}
}// 使用泛型接口
PairString, Integer p1 new OrderedPair(Even, 8);
PairString, String p2 new OrderedPair(hello, world);3、泛型方法
// 定义一个泛型方法
public class Util {public static K, V boolean compare(PairK, V p1, PairK, V p2) {return p1.getKey().equals(p2.getKey()) p1.getValue().equals(p2.getValue());}
}// 使用泛型方法
PairInteger, String p1 new OrderedPair(1, apple);
PairInteger, String p2 new OrderedPair(2, pear);
boolean same Util.compare(p1, p2);4、有界类型参数边界类型
public U extends Number void inspect(U u){System.out.println(T: u.getClass().getName());
}// 使用有界类型参数
inspect(123);
inspect(12.34);5、通配符类型
public void printBoxContent(Box? box){System.out.println(The box contains: box.get());
}// 使用通配符类型
BoxInteger integerBox new Box();
integerBox.set(10);BoxString stringBox new Box();
stringBox.set(Hello World);printBoxContent(integerBox);
printBoxContent(stringBox);三、无泛型 和 有泛型对比
在引入泛型之前Java 集合框架没有类型检查所以所有对象都被当做 Object 类型存储 以下是一个没有使用泛型的例子
import java.util.ArrayList;
import java.util.List;public class NonGenericExample {public static void main(String[] args) {List integerList new ArrayList();// 添加整数到集合integerList.add(new Integer(10)); // 自动装箱为 IntegerintegerList.add(new Integer(20));// 尝试添加一个字符串到整数列表// 编译器不会报错运行时也不会出错integerList.add(thirty); // 这是不安全的操作// 获取元素并进行类型转换Integer firstNumber (Integer) integerList.get(0);Integer secondNumber (Integer) integerList.get(1);// 这里尝试将第三个元素转换为 Integer但它实际上是一个 String// 这会引发 ClassCastExceptiontry {Integer thirdNumber (Integer) integerList.get(2);} catch (ClassCastException e) {System.out.println(Error: e.getMessage());}// 输出正确的整数值System.out.println(First number: firstNumber);System.out.println(Second number: secondNumber);}
}在上面的代码中我们创建了一个非泛型的 ArrayList并向其中添加了两个 Integer 对象和一个 String 对象。由于没有类型检查ArrayList 允许我们添加任何类型的对象而不会在编译时报错。但是当我们试图将 String 对象转换为 Integer 时程序会在运行时抛出 ClassCastException因为这是一个无效的转换。
引入泛型后我们可以在编译时期进行类型检查从而避免这种类型安全问题。下面是使用泛型的代码示例
import java.util.ArrayList;
import java.util.List;public class GenericExample {public static void main(String[] args) {ListInteger integerList new ArrayListInteger();// 添加整数到集合integerList.add(10); // 自动装箱为 IntegerintegerList.add(20);// 下面的代码会在编译时报错防止了运行时错误// integerList.add(thirty); // 编译错误// 获取元素时不需要进行类型转换Integer firstNumber integerList.get(0);Integer secondNumber integerList.get(1);// 输出正确的整数值System.out.println(First number: firstNumber);System.out.println(Second number: secondNumber);}
}在这个泛型版本的例子中如果尝试将一个不是 Integer 类型的对象添加到 integerList 中编译器将会报错从而保证了类型安全。这使得代码更安全更清晰也更容易维护。
四、泛型擦除
类型擦除是 Java 编译器应用的过程它允许泛型代码与不支持泛型的旧有 Java 代码兼容。在这个过程中编译器将泛型类型参数替换为它们的限定边界如果存在或者其他情况下替换为 Object。这意味着在编译后的 Java 字节码中所有的泛型类型信息都会丢失。
1、背景
Java在5.0版本中引入了泛型这使得开发者能够在编写集合类如List、Map等时指定集合中元素的类型例如 ListString 或 MapInteger, String。在引入泛型之前所有的集合中的对象都是 Object 类型这需要显式的类型转换并且可能导致运行时错误。
为了保证向后兼容Java的泛型是通过类型擦除来实现的。这意味着泛型信息只在编译阶段有效一旦代码被编译所有泛型类型参数都会被擦除替换为它们的限定边界类型如果有的话或者Object。这也意味着在运行时我们无法获取到泛型的类型参数信息。
2、泛型擦除实例
public class GenericErasureT {public void doSomething(T t) {System.out.println(t);}
}// 在编译后的class文件中geString 和 geInteger 类型实际上是相同的
// 它们都被擦除成了 GenericErasure 类型使用的是 Object 类型如下public class GenericErasure {public void doSomething(Object t) {System.out.println(t);}}如上编译后 泛型 T 变成了 Object泛型类型信息丢失转变成了 Object使得和不支持泛型的java代码一致。
3、限定边界
在Java泛型中“限定边界”Bounded Type是指对可以使用的泛型的类型参数进行限制。限定边界可以是一个特定的类或者是一个满足特定接口的任何类型。使用限定边界可以确保传递给泛型类型的类型参数满足某些基本要求这样就可以在类或方法内安全地调用定义在边界类型上的方法。
限定边界有两种类型
上界限定Upper Bounded Wildcards? extends Type 表示参数化类型可能是指定的类型或者是此类型的子类。下界限定Lower Bounded Wildcards? super Type 表示参数化类型可能是指定的类型或者是此类型的父类。
这里是针对上界限定的一个例子
假设你有一个类 Animal 和两个子类 Dog 和 Cat。你想要写一个方法这个方法可以接受 Animal 的任何子类的列表。你可以通过使用限定边界来实现
public class Animal {public void feed() {// ...}
}public class Dog extends Animal {// ...
}public class Cat extends Animal {// ...
}public class Main {public static void feedAnimals(List? extends Animal animals) {for (Animal a : animals) {a.feed();}}public static void main(String[] args) {ListDog dogs Arrays.asList(new Dog(), new Dog());ListCat cats Arrays.asList(new Cat(), new Cat());feedAnimals(dogs); // 正确dogs 是 Animal 的子类的 ListfeedAnimals(cats); // 正确cats 是 Animal 的子类的 List}
}在这个例子中feedAnimals 方法的参数 animals 使用了限定边界 ? extends Animal这意味着这个参数可以是 List、List、List 或任何 Animal 子类的列表。
当你没有具体的限定边界时你可以简单地使用 Object 类型因为在Java中所有的类都是 Object 的子类。如果你的泛型没有指定上界或下界它其实隐式的被限定为 Object。例如
public static void printList(List? list) {for (Object obj : list) {System.out.println(obj);}
}在这个例子中printList 方法接受一个未知类型的列表并且由于每个对象最终都继承自 Object 类我们可以安全地将列表中的每个元素作为 Object 输出。 总结 限定边界用来指定泛型类型参数必须继承自特定的父类或者实现特定的接口从而在编译时期就保证了类型安全。 如果没有特定的限定边界泛型类型默认是 Object。 4、结论
泛型提供了编译时类型安全检测机制预防了运行时报错异常问题。
泛型擦除保证了泛型代码与不支持泛型的旧有 Java 代码兼容