Trino lambda表达式使用学习小结

2022-05-20 08:52:18 浏览数 (1)

Trino中使用了很多的lambda表达式的写法,这点与Impala非常不同。这里简单学习了一些场景下的lambda表达式的用法。具体的例子如下:

代码语言:javascript复制
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这个类的内容,如下所示:

代码语言:javascript复制
@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代码,我们其实就可以拆解为如下的几行代码:

代码语言:javascript复制
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来实现,非常方便:

代码语言:javascript复制
@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特性,后续有时间再继续研究学习。

0 人点赞