将网站发布到微信小程序怎么做,百度小程序可以根据网站的要求做吗,WordPress自定义登录页面,个人做网站和百家号赚钱泛型
知识点
在Java高级开发中#xff0c;掌握泛型#xff08;Generics#xff09;是非常重要的#xff0c;它是Java语言中的一项重要特性#xff0c;提供了编译时类型安全检查机制#xff0c;使得代码更加灵活和可重用。以下是Java高级开发需要掌握的泛型知识点#…泛型
知识点
在Java高级开发中掌握泛型Generics是非常重要的它是Java语言中的一项重要特性提供了编译时类型安全检查机制使得代码更加灵活和可重用。以下是Java高级开发需要掌握的泛型知识点 泛型基础 理解泛型的定义和基本语法包括泛型类、泛型方法、泛型接口。泛型用法示例class MyClassT {}、public T void myMethod(T t) {}。 泛型通配符 理解 ?、? extends T、? super T 等通配符的含义和使用场景。了解上界通配符和下界通配符在泛型方法和泛型类中的应用。 泛型和继承关系 理解泛型在继承和子类化中的表现包括泛型类的继承、通配符的上下界限定。 类型擦除 理解Java泛型的类型擦除机制即在运行时泛型类型信息被擦除为原始类型。泛型类和泛型方法在编译后如何处理为非泛型形式的字节码。 泛型和集合 熟悉Java集合框架中泛型的应用如 ListT、MapK, V 等。掌握使用泛型提高集合类型安全性的方法。 泛型方法 理解泛型方法的定义和使用以及与泛型类的区别。了解泛型方法如何在静态方法和实例方法中应用。 泛型的好处 掌握泛型的优势如提高代码的类型安全性、避免类型转换错误、增加代码的可读性和重用性等。 泛型和反射 理解泛型和反射的结合使用如何通过反射获取泛型信息。 泛型约束 熟悉泛型的类型约束如泛型的边界限定extends 和 super 关键字。 泛型和异常 了解泛型和异常处理的结合使用如何处理泛型异常。
使用
1. 泛型类和泛型接口
限定类型可以在定义泛型类和泛型接口时使用。
// 限定类型 T 必须是 Number 的子类
public class NumberBoxT extends Number {private T content;public void setContent(T content) {this.content content;}public T getContent() {return content;}
}// 泛型接口示例
public interface NumberProcessorT extends Number {void process(T number);
}2. 泛型方法
在定义泛型方法时可以使用限定类型来约束方法的类型参数。
public class Utility {// 泛型方法限定类型 T 必须是 Number 的子类public static T extends Number void printNumber(T number) {System.out.println(Number: number);}
}3. 泛型通配符
限定类型也可以在泛型通配符中使用以表示方法参数或返回值的类型约束。
import java.util.List;public class WildcardExample {// 通配符上限list 中的元素必须是 Number 的子类public static void printNumbers(List? extends Number list) {for (Number number : list) {System.out.println(number);}}// 通配符下限list 中的元素必须是 Integer 的父类public static void addIntegers(List? super Integer list) {list.add(1);list.add(2);}
}4. 多重限定
如果类型参数需要同时满足多个接口可以使用 符号进行多重限定。
// T 必须同时实现 ComparableT 和 Serializable 接口
public class MultiBoundT extends ComparableT Serializable {private T content;public void setContent(T content) {this.content content;}public T getContent() {return content;}
}5. 示例业务场景
下面通过一些具体的业务场景示例来说明限定类型的使用。
示例 1: 数据处理器
假设我们有一个数据处理器需要处理不同类型的数值数据我们可以使用泛型类来实现。
public class DataProcessorT extends Number {private T data;public DataProcessor(T data) {this.data data;}public void process() {System.out.println(Processing data: data);}public T getData() {return data;}
}public class Main {public static void main(String[] args) {DataProcessorInteger intProcessor new DataProcessor(100);intProcessor.process();DataProcessorDouble doubleProcessor new DataProcessor(99.99);doubleProcessor.process();}
}示例 2: 数字打印工具
我们可以使用泛型方法来创建一个打印数字的工具方法。
public class PrintUtil {public static T extends Number void print(T number) {System.out.println(Number: number);}public static void main(String[] args) {print(123); // Integerprint(45.67); // Double}
}示例 3: 集合操作工具
使用泛型通配符来创建一个工具类用于操作集合中的元素。
import java.util.List;
import java.util.ArrayList;public class CollectionUtil {public static void printNumbers(List? extends Number list) {for (Number number : list) {System.out.println(Number: number);}}public static void addNumbers(List? super Integer list) {list.add(10);list.add(20);}public static void main(String[] args) {ListInteger intList new ArrayList();addNumbers(intList);printNumbers(intList);}
}结合具体场景的最佳实践
当然可以。以下是结合具体业务场景的Java泛型最佳实践讲解
1. 确保类型安全
业务场景一个电商系统需要存储和处理不同类型的订单如书籍订单和电子产品订单。
示例
class Order { /*...*/ }
class BookOrder extends Order { /*...*/ }
class ElectronicsOrder extends Order { /*...*/ }ListOrder orders new ArrayList();
orders.add(new BookOrder());
orders.add(new ElectronicsOrder());
// orders.add(new String()); // 编译时会报错防止插入非订单类型通过使用泛型确保集合中只存储订单对象防止错误类型的插入。
2. 使用通配符提高代码灵活性
业务场景需要处理一个包含各种类型商品的库存。
上界通配符Producer-Extends
public void printInventory(List? extends Product inventory) {for (Product p : inventory) {System.out.println(p.getName());}
}ListBook books new ArrayList();
ListElectronic electronics new ArrayList();
printInventory(books);
printInventory(electronics);通过上界通配符可以处理各种类型的商品清单。
下界通配符Consumer-Super
public void addElectronics(List? super Electronics inventory) {inventory.add(new Electronics());inventory.add(new Smartphone()); // Smartphone extends Electronics注意这里可以添加三种类父类自己子类多态
}ListProduct products new ArrayList();
addElectronics(products);通过下界通配符可以向商品列表中添加各种电子产品。
3. 避免使用原始类型
业务场景一个社交媒体应用需要存储用户评论。
示例
ListComment comments new ArrayList();
comments.add(new Comment(Great post!));
// comments.add(new User()); // 编译时会报错防止插入非评论类型通过使用泛型确保集合中只包含评论对象。
4. 使用泛型方法提高代码重用性
业务场景一个文件处理系统需要打印不同类型的文件内容。
示例
public T void printFiles(T[] files) {for (T file : files) {System.out.println(file.toString());}
}Document[] documents {new Document(Doc1), new Document(Doc2)};
Image[] images {new Image(Image1), new Image(Image2)};
printFiles(documents);
printFiles(images);通过使用泛型方法可以打印任何类型的文件内容提高代码重用性。
5. 使用有界类型参数进行约束
业务场景一个排行榜系统需要找到分数最高的用户。
示例
public T extends ComparableT T findTopScorer(T[] scores) {T top scores[0];for (T score : scores) {if (score.compareTo(top) 0) {top score;}}return top;
}Integer[] intScores {85, 92, 88};
Double[] doubleScores {85.5, 92.3, 88.9};
System.out.println(findTopScorer(intScores)); // 输出92
System.out.println(findTopScorer(doubleScores)); // 输出92.3通过使用有界类型参数可以确保数组中的元素可以比较从而找到最高分数。
6. 避免使用泛型类型的静态成员
业务场景一个用户管理系统需要存储用户信息。
示例
public class UserManagerT {private T user;// private static T instance; // 编译错误避免使用泛型类型的静态成员
}通过避免使用泛型类型的静态成员防止类型擦除带来的问题。
7. 使用类型令牌解决类型擦除问题
业务场景一个对象工厂需要根据类型创建对象实例。
示例
public T T createInstance(ClassT clazz) throws Exception {return clazz.getDeclaredConstructor().newInstance();
}User user createInstance(User.class);通过使用类型令牌可以在运行时获取泛型类型信息创建对象实例。
8. 避免在泛型类中使用泛型数组
业务场景一个订单管理系统需要存储不同类型的订单。
示例
public class OrderManagerT {private T[] orders; // 编译错误避免使用泛型数组private ListT orderList new ArrayList();
}通过避免使用泛型数组防止类型擦除带来的问题。
9. 谨慎使用泛型和异常
业务场景一个数据处理系统需要处理不同类型的数据。
示例
// 错误示例
public T extends Exception void processData() throws T {// 不能抛出或捕获泛型异常类型
}避免抛出或捕获泛型异常类型。
10. 使用PECS原则
业务场景一个物流系统需要处理和添加不同类型的货物。
示例
// Producer-Extends
public void processCargo(List? extends Cargo cargoList) {for (Cargo cargo : cargoList) {System.out.println(cargo.getDetails());}
}// Consumer-Super
public void addCargo(List? super PerishableCargo cargoList) {cargoList.add(new PerishableCargo());cargoList.add(new FreshCargo()); // FreshCargo extends PerishableCargo
}ListCargo cargos new ArrayList();
addCargo(cargos);通过PECS原则可以处理和添加不同类型的货物。
泛型的实现原理及其在运行时的表现
1. 泛型的本质
Java 泛型在编译时提供类型检查和类型安全允许开发人员编写更灵活且类型安全的代码。然而在运行时Java 泛型会被类型擦除Type Erasure这意味着所有的类型参数都会被擦除并替换为它们的限定类型如果没有指定则替换为 Object。
2. 类型擦除Type Erasure
类型擦除是 Java 泛型的核心机制。在编译时编译器会移除泛型类型信息并插入必要的类型转换以确保类型安全。在运行时泛型类型信息不存在所有泛型类型都被替换为原始类型。
例如以下泛型类
public class BoxT {private T content;public void setContent(T content) {this.content content;}public T getContent() {return content;}
}在编译后类型参数 T 会被替换为 Object
public class Box {private Object content;public void setContent(Object content) {this.content content;}public Object getContent() {return content;}
}编译器在插入和取出元素时会生成适当的类型转换代码以确保类型安全。
3. 限定类型
如果泛型类型参数有上限限制如 T extends Number类型擦除后会替换为限定类型而不是 Object。
例如
public class NumberBoxT extends Number {private T content;public void setContent(T content) {this.content content;}public T getContent() {return content;}
}在编译后类型参数 T 会被替换为 Number
public class NumberBox {private Number content;public void setContent(Number content) {this.content content;}public Number getContent() {return content;}
}4. 泛型方法
泛型方法在运行时也会经历类型擦除其类型参数在运行时被替换为限定类型或 Object。
public static T void printArray(T[] array) {for (T element : array) {System.out.println(element);}
}在编译后类型参数 T 被替换为 Object
public static void printArray(Object[] array) {for (Object element : array) {System.out.println(element);}
}5. 泛型数组
Java 不允许创建泛型类型的数组因为在运行时泛型类型被擦除数组的运行时类型需要具体的类型信息。
以下代码是非法的
ListString[] listArray new ListString[10]; // 编译错误解决方法是使用通配符或 Object
List?[] listArray new List?[10];6. 泛型与反射
由于类型擦除在使用反射时无法获取泛型类型参数的具体类型信息。例如
ListString list new ArrayList();
Type type list.getClass().getGenericSuperclass();
System.out.println(type); // 输出 java.util.AbstractListE上面的代码只能得到泛型类型参数 E而不是具体的 String 类型。 为什么输出 java.util.AbstractListE ArrayList 继承自 AbstractList而 AbstractList 又是AbstractCollection 的子类。具体的继承关系如下 ArrayListE extends AbstractListEAbstractListE extends AbstractCollectionE 当你调用 list.getClass().getGenericSuperclass() 时getGenericSuperclass() 方法返回的是直接父类的类型即 AbstractListE。这个类型包含泛型信息但因为泛型类型参数 E 是在编译时擦除的实际运行时显示的是泛型类型 E。 小结
Java 泛型通过类型擦除实现在编译时确保类型安全但在运行时移除类型信息。类型擦除机制使得 Java 泛型在运行时没有性能开销并且与非泛型代码兼容。然而这也导致在运行时无法获取具体的泛型类型信息需要通过其他方式如反射来处理泛型类型。
类型擦除的例外
在 Java 中泛型类型信息确实会在编译期间被擦除这就是所谓的类型擦除type erasure。类型擦除的基本概念是泛型参数类型在编译时被替换为它们的非泛型上界通常是 Object除非有特定的边界并在必要时插入类型转换。类型擦除的结果是在运行时泛型参数的实际类型信息是不可用的。然而有一些情况下类型信息是可以保留的主要依赖于编译器在生成 class 文件时的额外信息。以下是几种情况
字段类的字段声明为泛型时泛型信息会存储在 class 文件的字段信息部分。方法参数和返回类型方法参数或返回类型使用泛型时泛型信息会存储在 class 文件的方法签名部分。类和接口类或接口本身声明为泛型时泛型信息会存储在 class 文件的类型信息部分。
1. 字段的泛型类型
当一个类的字段声明为泛型类型时编译器会将泛型类型的信息存储在 class 文件的字段信息部分。这使得在运行时通过反射 API 可以获取到这些泛型类型的信息。比如
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;public class GenericField {private ListString stringList;public static void main(String[] args) throws NoSuchFieldException {Field field GenericField.class.getDeclaredField(stringList);Type genericFieldType field.getGenericType();if (genericFieldType instanceof ParameterizedType) {ParameterizedType parameterizedType (ParameterizedType) genericFieldType;Type[] actualTypeArguments parameterizedType.getActualTypeArguments();for (Type type : actualTypeArguments) {System.out.println(type);}}}
}在这个示例中stringList 字段的泛型类型信息在编译期被存储在 class 文件中虽然在运行时无法直接使用泛型类型但可以通过反射 API 获取到这些信息。输出结果为 class java.lang.String。
2. 方法的泛型参数类型
类似地当方法的参数或返回类型使用泛型时编译器会在 class 文件中存储这些泛型信息。在运行时可以通过反射获取这些信息。比如
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;public class GenericMethod {public T void printList(ListT list) {// Method body}public static void main(String[] args) throws NoSuchMethodException {Method method GenericMethod.class.getMethod(printList, List.class);Type[] genericParameterTypes method.getGenericParameterTypes();for (Type type : genericParameterTypes) {if (type instanceof ParameterizedType) {ParameterizedType parameterizedType (ParameterizedType) type;Type[] actualTypeArguments parameterizedType.getActualTypeArguments();for (Type actualType : actualTypeArguments) {System.out.println(actualType);}}}}
}在这个示例中printList 方法的参数 list 的泛型类型信息被存储在 class 文件中可以在运行时通过反射获取。
总结
类型擦除确实会在运行时丢失泛型参数的具体类型信息但是编译器会在 class 文件中存储一些必要的泛型信息使得可以在运行时通过反射获取这些信息。因此通过 ParameterizedType可以在运行时获取泛型参数的类型信息这在某些情况下非常有用。
ParameterizedType
ParameterizedType 是 Java 反射 API 中的一个接口它表示一个带有实际类型参数的泛型类型。在实际开发中我们经常会遇到需要在运行时获取泛型类型参数的情况这时 ParameterizedType 就非常有用。
ParameterizedType 接口用于表示参数化类型。一个参数化类型是指带有实际类型参数的类型比如 ListString、MapString, Integer 等。通过反射 API可以在运行时获取这些参数化类型的实际类型参数。
常用方法
ParameterizedType 接口定义了一些方法用于获取参数化类型的详细信息
Type[] getActualTypeArguments()返回实际类型参数的数组。Type getRawType()返回不带泛型参数的原始类型。Type getOwnerType()返回这个类型的所有者类型如果这个类型是一个内部类的话。
使用示例
下面通过一个具体的例子来说明如何使用 ParameterizedType 获取泛型类型参数。
示例 1获取类的泛型类型参数
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;public class GenericClassT {public static void main(String[] args) {// 使用匿名子类来保留泛型类型信息GenericClassListString instance new GenericClassListString() {};Type superclass instance.getClass().getGenericSuperclass();if (superclass instanceof ParameterizedType) {ParameterizedType parameterizedType (ParameterizedType) superclass;Type[] actualTypeArguments parameterizedType.getActualTypeArguments();for (Type type : actualTypeArguments) {System.out.println(type);}}}
}在这个示例中通过创建 GenericClass 的匿名子类我们保留了泛型类型信息。然后使用反射获取泛型类型参数输出结果为 java.util.Listjava.lang.String。
示例 2获取方法的泛型类型参数
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;public class GenericMethod {public T void printList(ListT list) {// Method body}public static void main(String[] args) throws NoSuchMethodException {Method method GenericMethod.class.getMethod(printList, List.class);Type[] genericParameterTypes method.getGenericParameterTypes();for (Type type : genericParameterTypes) {if (type instanceof ParameterizedType) {ParameterizedType parameterizedType (ParameterizedType) type;Type[] actualTypeArguments parameterizedType.getActualTypeArguments();for (Type actualType : actualTypeArguments) {System.out.println(actualType);}}}}
}在这个示例中我们通过反射获取了泛型方法 printList 的类型参数输出结果为 T。
示例 3获取字段的泛型类型参数
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;public class GenericField {private ListString stringList;public static void main(String[] args) throws NoSuchFieldException {Field field GenericField.class.getDeclaredField(stringList);Type genericFieldType field.getGenericType();if (genericFieldType instanceof ParameterizedType) {ParameterizedType parameterizedType (ParameterizedType) genericFieldType;Type[] actualTypeArguments parameterizedType.getActualTypeArguments();for (Type type : actualTypeArguments) {System.out.println(type);}}}
}在这个示例中我们通过反射获取了泛型字段 stringList 的类型参数输出结果为 java.lang.String。