面试题答案
一键面试1. 获取泛型方法的参数化类型
- 困难:在Java中,由于类型擦除,泛型类型信息在运行时会被擦除,导致直接通过反射获取泛型方法的参数化类型变得困难。例如,对于方法
public <T> void genericMethod(T param)
,在运行时无法直接得知T
的具体类型。 - 解决方案:
- 使用ParameterizedType接口:
- 当方法的参数类型是泛型时,可以通过
Method
对象获取其参数类型数组,然后检查每个参数类型是否为ParameterizedType
。如果是,则可以通过ParameterizedType
获取实际的参数化类型。
import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; public class GenericReflectionExample { public void genericMethod(List<String> list) {} public static void main(String[] args) throws NoSuchMethodException { Method method = GenericReflectionExample.class.getMethod("genericMethod", List.class); Type[] parameterTypes = method.getGenericParameterTypes(); for (Type type : parameterTypes) { if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println("实际参数化类型: " + actualTypeArgument); } } } } }
- 当方法的参数类型是泛型时,可以通过
- 借助Signature属性:在一些情况下,可以通过读取字节码中的Signature属性来获取泛型信息。但这需要使用如ASM等字节码操作库,相对复杂。
- 使用ParameterizedType接口:
2. 运行时动态创建带有泛型参数的对象
- 困难:由于类型擦除,在运行时动态创建带有泛型参数的对象时,编译器无法确保创建的对象实际具有正确的泛型类型。例如,
List<String>
和List<Integer>
在运行时是同一种类型List
。 - 解决方案:
- 使用TypeToken:Guava库提供了
TypeToken
类,可以用来在运行时持有泛型类型信息。import com.google.common.reflect.TypeToken; import java.lang.reflect.Type; import java.util.List; public class TypeTokenExample { public static void main(String[] args) { Type listOfStringType = new TypeToken<List<String>>() {}.getType(); System.out.println("类型: " + listOfStringType); } }
- 自定义泛型工厂:通过定义一个泛型工厂类,在创建对象时传递实际的类型参数。
import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; class GenericFactory<T> { private Class<T> type; public GenericFactory(Class<T> type) { this.type = type; } public T createInstance() { try { return type.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } } public class GenericObjectCreation { public static void main(String[] args) { GenericFactory<String> stringFactory = new GenericFactory<>(String.class); String str = stringFactory.createInstance(); System.out.println(str); } }
- 使用TypeToken:Guava库提供了
3. 处理类型擦除问题
- 困难:类型擦除会导致运行时丢失泛型类型信息,使得一些依赖泛型类型的操作变得困难,如泛型数组的创建,以及在反射中获取准确的泛型类型。
- 解决方案:
- 使用通配符和受限类型:在定义泛型方法或类时,使用通配符
?
和受限类型,如<? extends Number>
或<? super Integer>
,可以在一定程度上缓解类型擦除带来的问题,同时保持代码的灵活性。 - 保留泛型信息的自定义注解:通过自定义注解,可以在编译时将泛型信息保留下来,并在运行时通过反射读取注解来获取泛型信息。例如,可以定义一个注解
@GenericInfo
,在使用泛型的地方添加该注解并指定泛型类型,然后在运行时通过反射获取注解信息。
- 使用通配符和受限类型:在定义泛型方法或类时,使用通配符