泛型方法
泛型方法在开源项目中应用的非常广泛,通过本文的介绍希望帮助读者更快地理解源码以及可以在平时的开发中能够熟练使用泛型方法,以下对泛型方法的说明,摘录于《Thinking in Java》第四版
You can also parameterize methods within a class. The class itself may or may not be generic—this is independent of whether you have a generic method.
A generic method allows the method to vary independently of the class. As a guideline, you should use generic methods "whenever you can." That is, if it’s possible to make a method generic rather than the entire class, it’s probably going to be clearer to do so. In addition, if a method is static, it has no access to the generic type parameters of the class, so if it needs to use genericity it must be a generic method.
To define a generic method, you simply place a generic parameter list before the return value.
从上面的说明中可以总结出以下几点:
- 定义泛型方法,只需将泛型参数列表置于返回值之前;
- 泛型方法可以在泛型类中也可以在非泛型类中,与其所在的类是否是泛型没有关系;
- 对于一个static的方法,是无法访问泛型类的类型参数,也就是说,如果static方法需要使用泛型能力,就必须使其成为泛型方法;
- 如果使用泛型方法可以取代将整个类泛型化,就应该只使用泛型方法。
下面以Apache Flink 1.14.0源码为例来说明,泛型方法在flink中是如何使用的。
源码示例
泛型方法的定义
HadoopSecurityContext类中的runSecured方法就是一个泛型方法。
在public
和T
之间加入了<T>
说明runSecured是一个泛型方法;HadoopSecurityContext类不是泛型类而且SecurityContext接口也不是泛型接口,说明泛型方法的使用和类本身是否是泛型类无关。
package org.apache.flink.runtime.security.contexts;
public class HadoopSecurityContext implements SecurityContext {
private final UserGroupInformation ugi;
public HadoopSecurityContext(UserGroupInformation ugi) {
this.ugi = Preconditions.checkNotNull(ugi, "UGI passed cannot be null");
}
public <T> T runSecured(final Callable<T> securedCallable) throws Exception {
return ugi.doAs((PrivilegedExceptionAction<T>) securedCallable::call);
}
}
runSecured到底返回的类型T是什么,是由该方法参数中的T决定的,例如
org.apache.flink.runtime.taskexecutor.TaskManagerRunner
中runTaskManagerProcessSecurely方法中runSecured的返回值类型就是runTaskManager方法的返回值类型,即int类型
exitCode =
SecurityUtils.getInstalledContext()
.runSecured(() -> runTaskManager(configuration, pluginManager));
而在org.apache.flink.runtime.entrypoint.ClusterEntrypoint
中startCluster方法中返回值就是runSecured的返回值类型就是runCluster方法的返回值类型,即void
securityContext.runSecured(
(Callable<Void>)
() -> {
runCluster(configuration, pluginManager);
return null;
});
再看下org.apache.flink.api.java.tuple.Tuple2
,这是一个泛型类,public class Tuple2<T0, T1> extends Tuple
里面getField方法就是一个泛型方法
public <T> T getField(int pos) {
switch (pos) {
case 0:
return (T) this.f0;
case 1:
return (T) this.f1;
default:
throw new IndexOutOfBoundsException(String.valueOf(pos));
}
}
但是注意,这个方法的类型变量是T
和这个类的类型变量T0
和T1
并不一样,也就是说,泛型方法的类型变量与所在泛型类无关,可以与类的类型变量相同也可以不同。
静态泛型方法
最后看一下org.apache.flink.connector.kafka.source.KafkaSourceOptions
中的getOption方法,是静态方法也是泛型方法
public static <T> T getOption(
Properties props, ConfigOption<?> configOption, Function<String, T> parser) {
String value = props.getProperty(configOption.key());
return (T) (value == null ? configOption.defaultValue() : parser.apply(value));
}
同样此方法的返回值类型是由参数中的T
决定的,可以看下这个方法是如何被调用的
KafkaPartitionSplitReader#maybeRegisterKafkaConsumerMetrics方法中返回的是Boolean类型
final Boolean needToRegister =
KafkaSourceOptions.getOption(
props,
KafkaSourceOptions.REGISTER_KAFKA_CONSUMER_METRICS,
Boolean::parseBoolean);
KafkaSourceEnumerator类的构造方法中返回的是Long类型
this.partitionDiscoveryIntervalMs =
KafkaSourceOptions.getOption(
properties,
KafkaSourceOptions.PARTITION_DISCOVERY_INTERVAL_MS,
Long::parseLong);