You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Lambda与泛型结合问题的优雅解决方案探讨

泛型与Lambda结合的优雅实现方案

我正在寻找一种更优雅的解决方案,核心问题是将Lambda表达式与泛型结合使用。根据《Java语言规范》(JLS),我想要的实现方式无法直接达成。因此想请教:是否存在更优雅的处理方式?有没有相关建议或通用实践?

我的自定义List类MyListImpl<T>中,大量方法需要用到BiPredicate<T, T>:

package stackoverflow.genericlambdas;

import java.util.ArrayList;
import java.util.function.BiPredicate;

public class MyListImpl<T> {

    private final ArrayList<T> mItems = new ArrayList<>();


    public void addItem(final T pItem) {
        mItems.add(pItem);
    }

    // 假设我的库中有大量这类方法
    public int getFirstIndexOf(final BiPredicate<T, T> pIdentifier, final T pItem) {
        for (int i = 0; i < mItems.size(); i++) {
            final T t = mItems.get(i);
            if (pIdentifier.test(t, pItem)) return i;
        }
        return -1; // 未找到
    }

}

我希望在Library类中存储通用的BiPredicate<T, T>模板,但直接定义泛型Lambda常量会出现编译错误,目前只能通过笨拙的内部类实现:

package stackoverflow.genericlambdas;

import java.util.Objects;
import java.util.function.BiPredicate;

public class Library {


    static public class CLUMSY_IDENTITY_CLASS<T> implements BiPredicate<T, T> {
        @Override public boolean test(final T p, final T q) {
            return p == q;
        }
    }
    static public class CLUMSY_EQUALITY_CLASS<T> implements BiPredicate<T, T> {
        @Override public boolean test(final T p, final T q) {
            return Objects.equals(p, q);
        }
    }


    static public final BiPredicate<T, T>   IDENTITY    = (p, q) -> p == q;                 // 编译错误
    static public final BiPredicate<T, T>   EQUALITY    = (p, q) -> Objects.equals(p, q);   // 编译错误


}

在使用类GenericLamdasUse中,我期望直接调用Library.EQUALITY这类常量,但会触发编译错误;目前可用的方式要么每次写Lambda,要么重复实例化内部类,不够优雅:

package stackoverflow.genericlambdas;

import java.util.Objects;

public class GenericLamdasUse {

    public static void main(final String[] args) {
        final MyListImpl<String> list = new MyListImpl<>();
        list.addItem("aa");
        list.addItem("bb");
        list.addItem("cc");

        final String testItem = "cc";
        final String testItem2 = "c" + (Math.random() < 2 /* 永远为true */ ? "c" : ""); // 构建的新字符串与编译期常量"cc"不是同一引用

        final int whatIKnowWorks1 = list.getFirstIndexOf((p, q) -> Objects.equals(p, q), testItem);
        final int whatIKnowWorks2 = list.getFirstIndexOf((p, q) -> p == q, testItem);

        final int whatIKnowWorks3 = list.getFirstIndexOf(new Library.CLUMSY_IDENTITY_CLASS<>(), testItem); // 不想每次都重新实例化
        final int whatIKnowWorks4 = list.getFirstIndexOf(new Library.CLUMSY_EQUALITY_CLASS<>(), testItem); // 每次使用都要实例化太麻烦

        final Library.CLUMSY_IDENTITY_CLASS<String> clumsy5 = new Library.CLUMSY_IDENTITY_CLASS<>(); // 不想重复实例化
        final Library.CLUMSY_EQUALITY_CLASS<String> clumsy6 = new Library.CLUMSY_EQUALITY_CLASS<>();
        final int whatIKnowWorks5 = list.getFirstIndexOf(clumsy5, testItem); // 每次使用都要这么做
        final int whatIKnowWorks6 = list.getFirstIndexOf(clumsy6, testItem); // 而且每次都要匹配<String>类型

        final int whatIWant1 = list.getFirstIndexOf(Library.EQUALITY, testItem); // 我想要这种用法,但会编译错误
        final int whatIWant2 = list.getFirstIndexOf(Library.IDENTITY, testItem); // 我想要这种用法,但会编译错误
    }

}

优雅解决方案:泛型静态方法返回Lambda

Java不允许直接定义泛型的静态字段,但可以通过泛型静态方法返回对应的Lambda实现,既避免重复实例化,又能自动推导泛型类型:

修改Library类如下:

package stackoverflow.genericlambdas;

import java.util.Objects;
import java.util.function.BiPredicate;

public class Library {

    // 泛型静态方法,返回引用相等的BiPredicate
    @SuppressWarnings("unchecked")
    public static <T> BiPredicate<T, T> identity() {
        // 利用匿名内部类的单例特性,避免重复实例化
        return (BiPredicate<T, T>) IDENTITY_HOLDER;
    }

    // 泛型静态方法,返回值相等的BiPredicate
    @SuppressWarnings("unchecked")
    public static <T> BiPredicate<T, T> equality() {
        return (BiPredicate<T, T>) EQUALITY_HOLDER;
    }

    // 私有静态常量,存储无泛型的Lambda实例(Lambda本身无泛型,泛型由调用时推导)
    private static final BiPredicate<?, ?> IDENTITY_HOLDER = (p, q) -> p == q;
    private static final BiPredicate<?, ?> EQUALITY_HOLDER = (p, q) -> Objects.equals(p, q);
}

使用方式

在GenericLamdasUse中可以直接调用,编译器会自动推导泛型类型:

// 直接调用静态方法,无需指定类型,无需重复实例化
final int whatIWantNow1 = list.getFirstIndexOf(Library.equality(), testItem);
final int whatIWantNow2 = list.getFirstIndexOf(Library.identity(), testItem);

方案优势

  • 避免重复实例化内部类的开销,内部HOLDER常量只会初始化一次
  • 调用时无需显式指定泛型类型,编译器自动推导,代码简洁
  • 相比每次写Lambda,复用性更强,逻辑统一维护

另一种简化方案:利用Java 8+的方法引用

如果只是需要Objects.equals这类已有方法的适配,还可以直接使用方法引用,代码更简洁:

// 直接使用方法引用替代Lambda
final int index = list.getFirstIndexOf(Objects::equals, testItem);

这种方式适合简单的已有方法适配,无需额外定义Library类。


内容的提问来源于stack exchange,提问作者JayC667

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 10:20:03