Trino中使用了很多的lambda表达式的写法,这点与Impala非常不同。这里简单学习了一些场景下的lambda表达式的用法。具体的例子如下:
private final JdbcApiStats getTableNames = new JdbcApiStats();
public JdbcApiStats getGetTableNames()
{
return getTableNames;
}
public List<SchemaTableName> getTableNames(ConnectorSession session, Optional<String> schema)
{
return stats.getGetTableNames().wrap(() -> delegate().getTableNames(session, schema));
}
函数本体只有一行代码,但是却完成了多个动作,这里主要用到的就是lambda表达式的写法,新手阅读起来会非常困难,我们一步一步拆解来看。首先通过getGetTableNames()方法获取到了一个JdbcApiStats类型的变量,然后调用其wrap方法。传入的参数是一个lambda表达式,即() -> delegate().getTableNames(session, schema)。这个表达式不需要参数,返回值是一个List集合。我们看下JdbcApiStats这个类的内容,如下所示:
@ThreadSafe
public class JdbcApiStats
{
private final TimeStat time = new TimeStat(MILLISECONDS);
private final CounterStat failures = new CounterStat();
public <V, E extends Exception> V wrap(ThrowingCallable<V, E> callable)
throws E
{
try (TimeStat.BlockTimer ignored = time.time()) {
return callable.call();
}
catch (Exception e) {
failures.update(1);
throw e;
}
}
public <E extends Exception> void wrap(ThrowingRunnable<E> callable)
throws E
{
try (TimeStat.BlockTimer ignored = time.time()) {
callable.run();
}
catch (Exception e) {
failures.update(1);
throw e;
}
}
@Managed
@Nested
public TimeStat getTime()
{
return time;
}
@Managed
@Nested
public CounterStat getFailures()
{
return failures;
}
public interface ThrowingCallable<V, E extends Exception>
{
V call() throws E;
}
public interface ThrowingRunnable<E extends Exception>
{
void run() throws E;
}
}
这里针对有返回值和无返回值的情况,定义了两个interface,分别是:ThrowingCallable和ThrowingRunnable,分别只有一个abstract method,这就是我们通常所说的函数式接口,有且仅有一个抽象方法。然后用这两个interface分别作为参数,构造了两个wrap函数。同时,进行了一些时间和失败次数的状态统计。再回到最初的labmda代码,我们其实就可以拆解为如下的几行代码:
public List<SchemaTableName> getTableNames(ConnectorSession session, Optional<String> schema)
{
JdbcApiStats.ThrowingCallable callable = () -> delegate().getTableNames(session, schema);
List<SchemaTableName> list = (List<SchemaTableName>) stats.getGetTableNames().wrap(callable);
return list;
}
可以看到,这里将lambda显示转换为了ThrowingCallable这个interface类型的变量,然后传入到wrap函数。最后,将返回值显示转换为List,这里对应的就是ThrowingCallable中的V。在原先的写法中,编译器可以直接就从delegate().getTableNames()推导出返回值的类型,因此不需要显示转换。而ThrowingCallable中的call也就对应了delegate().getTableNames(),这里函数名称不一定必须是call,也可以换成其他的名称,但是只能有一个abstract method。如果我们再增加一个abstract method,例如V call2() throws E,编辑器就会报错,如下所示:
这样的使用好处就是,对于不同动作的相同wrap,就可以直接通过同一个JdbcApiStats来实现,非常方便:
@Override
public Optional<JdbcTableHandle> getTableHandle(ConnectorSession session, SchemaTableName schemaTableName)
{
return stats.getGetTableHandle().wrap(() -> delegate().getTableHandle(session, schemaTableName));
}
@Override
public List<JdbcColumnHandle> getColumns(ConnectorSession session, JdbcTableHandle tableHandle)
{
return stats.getGetColumns().wrap(() -> delegate().getColumns(session, tableHandle));
}
Trino的代码中用了很多函数式编程和lambda特性,后续有时间再继续研究学习。