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
相关产品推荐
相关产品推荐

